diff options
Diffstat (limited to 'drivers/gpu/drm')
174 files changed, 13942 insertions, 3153 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 1e82882..19b8e0d 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -220,3 +220,5 @@ source "drivers/gpu/drm/tegra/Kconfig" source "drivers/gpu/drm/omapdrm/Kconfig" source "drivers/gpu/drm/tilcdc/Kconfig" + +source "drivers/gpu/drm/qxl/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0d59b24..6a42115 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -52,4 +52,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ +obj-$(CONFIG_DRM_QXL) += qxl/ obj-y += i2c/ diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig index a277b12..da4a51e 100644 --- a/drivers/gpu/drm/ast/Kconfig +++ b/drivers/gpu/drm/ast/Kconfig @@ -1,6 +1,6 @@ config DRM_AST tristate "AST server chips" - depends on DRM && PCI && EXPERIMENTAL + depends on DRM && PCI select DRM_TTM select FB_SYS_COPYAREA select FB_SYS_FILLRECT diff --git a/drivers/gpu/drm/cirrus/Kconfig b/drivers/gpu/drm/cirrus/Kconfig index fc154dd..bf67b22 100644 --- a/drivers/gpu/drm/cirrus/Kconfig +++ b/drivers/gpu/drm/cirrus/Kconfig @@ -1,6 +1,6 @@ config DRM_CIRRUS_QEMU tristate "Cirrus driver for QEMU emulated device" - depends on DRM && PCI && EXPERIMENTAL + depends on DRM && PCI select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index a575cb2..bb8f580 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -105,12 +105,11 @@ drm_clflush_sg(struct sg_table *st) { #if defined(CONFIG_X86) if (cpu_has_clflush) { - struct scatterlist *sg; - int i; + struct sg_page_iter sg_iter; mb(); - for_each_sg(st->sgl, sg, st->nents, i) - drm_clflush_page(sg_page(sg)); + for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) + drm_clflush_page(sg_page_iter_page(&sg_iter)); mb(); return; diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 45adf97..725968d 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -74,24 +74,13 @@ void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle) */ static int drm_ctxbitmap_next(struct drm_device * dev) { - int new_id; int ret; -again: - if (idr_pre_get(&dev->ctx_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Out of memory expanding drawable idr\n"); - return -ENOMEM; - } mutex_lock(&dev->struct_mutex); - ret = idr_get_new_above(&dev->ctx_idr, NULL, - DRM_RESERVED_CONTEXTS, &new_id); + ret = idr_alloc(&dev->ctx_idr, NULL, DRM_RESERVED_CONTEXTS, 0, + GFP_KERNEL); mutex_unlock(&dev->struct_mutex); - if (ret == -EAGAIN) - goto again; - else if (ret) - return ret; - - return new_id; + return ret; } /** @@ -118,7 +107,7 @@ int drm_ctxbitmap_init(struct drm_device * dev) void drm_ctxbitmap_cleanup(struct drm_device * dev) { mutex_lock(&dev->struct_mutex); - idr_remove_all(&dev->ctx_idr); + idr_destroy(&dev->ctx_idr); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3bdf2a6..957fb70 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -266,32 +266,21 @@ char *drm_get_connector_status_name(enum drm_connector_status status) static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type) { - int new_id = 0; int ret; -again: - if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Ran out memory getting a mode number\n"); - return -ENOMEM; - } - mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); - - if (!ret) { + ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL); + if (ret >= 0) { /* * Set up the object linking under the protection of the idr * lock so that other users can't see inconsistent state. */ - obj->id = new_id; + obj->id = ret; obj->type = obj_type; } mutex_unlock(&dev->mode_config.idr_mutex); - if (ret == -EAGAIN) - goto again; - - return ret; + return ret < 0 ? ret : 0; } /** @@ -423,7 +412,7 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, mutex_lock(&dev->mode_config.fb_lock); fb = __drm_framebuffer_lookup(dev, id); if (fb) - kref_get(&fb->refcount); + drm_framebuffer_reference(fb); mutex_unlock(&dev->mode_config.fb_lock); return fb; @@ -1131,44 +1120,6 @@ int drm_mode_create_dirty_info_property(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_create_dirty_info_property); -/** - * drm_mode_config_init - initialize DRM mode_configuration structure - * @dev: DRM device - * - * Initialize @dev's mode_config structure, used for tracking the graphics - * configuration of @dev. - * - * Since this initializes the modeset locks, no locking is possible. Which is no - * problem, since this should happen single threaded at init time. It is the - * driver's problem to ensure this guarantee. - * - */ -void drm_mode_config_init(struct drm_device *dev) -{ - mutex_init(&dev->mode_config.mutex); - mutex_init(&dev->mode_config.idr_mutex); - mutex_init(&dev->mode_config.fb_lock); - INIT_LIST_HEAD(&dev->mode_config.fb_list); - INIT_LIST_HEAD(&dev->mode_config.crtc_list); - INIT_LIST_HEAD(&dev->mode_config.connector_list); - INIT_LIST_HEAD(&dev->mode_config.encoder_list); - INIT_LIST_HEAD(&dev->mode_config.property_list); - INIT_LIST_HEAD(&dev->mode_config.property_blob_list); - INIT_LIST_HEAD(&dev->mode_config.plane_list); - idr_init(&dev->mode_config.crtc_idr); - - drm_modeset_lock_all(dev); - drm_mode_create_standard_connector_properties(dev); - drm_modeset_unlock_all(dev); - - /* Just to be sure */ - dev->mode_config.num_fb = 0; - dev->mode_config.num_connector = 0; - dev->mode_config.num_crtc = 0; - dev->mode_config.num_encoder = 0; -} -EXPORT_SYMBOL(drm_mode_config_init); - int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) { uint32_t total_objects = 0; @@ -1214,70 +1165,6 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev, EXPORT_SYMBOL(drm_mode_group_init_legacy_group); /** - * drm_mode_config_cleanup - free up DRM mode_config info - * @dev: DRM device - * - * Free up all the connectors and CRTCs associated with this DRM device, then - * free up the framebuffers and associated buffer objects. - * - * Note that since this /should/ happen single-threaded at driver/device - * teardown time, no locking is required. It's the driver's job to ensure that - * this guarantee actually holds true. - * - * FIXME: cleanup any dangling user buffer objects too - */ -void drm_mode_config_cleanup(struct drm_device *dev) -{ - struct drm_connector *connector, *ot; - struct drm_crtc *crtc, *ct; - struct drm_encoder *encoder, *enct; - struct drm_framebuffer *fb, *fbt; - struct drm_property *property, *pt; - struct drm_plane *plane, *plt; - - list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, - head) { - encoder->funcs->destroy(encoder); - } - - list_for_each_entry_safe(connector, ot, - &dev->mode_config.connector_list, head) { - connector->funcs->destroy(connector); - } - - list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, - head) { - drm_property_destroy(dev, property); - } - - /* - * Single-threaded teardown context, so it's not required to grab the - * fb_lock to protect against concurrent fb_list access. Contrary, it - * would actually deadlock with the drm_framebuffer_cleanup function. - * - * Also, if there are any framebuffers left, that's a driver leak now, - * so politely WARN about this. - */ - WARN_ON(!list_empty(&dev->mode_config.fb_list)); - list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { - drm_framebuffer_remove(fb); - } - - list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, - head) { - plane->funcs->destroy(plane); - } - - list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { - crtc->funcs->destroy(crtc); - } - - idr_remove_all(&dev->mode_config.crtc_idr); - idr_destroy(&dev->mode_config.crtc_idr); -} -EXPORT_SYMBOL(drm_mode_config_cleanup); - -/** * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo * @out: drm_mode_modeinfo struct to return to the user * @in: drm_display_mode to use @@ -2338,7 +2225,6 @@ int drm_mode_addfb(struct drm_device *dev, fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - drm_modeset_unlock_all(dev); return PTR_ERR(fb); } @@ -2518,7 +2404,6 @@ int drm_mode_addfb2(struct drm_device *dev, fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - drm_modeset_unlock_all(dev); return PTR_ERR(fb); } @@ -4078,3 +3963,110 @@ int drm_format_vert_chroma_subsampling(uint32_t format) } } EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); + +/** + * drm_mode_config_init - initialize DRM mode_configuration structure + * @dev: DRM device + * + * Initialize @dev's mode_config structure, used for tracking the graphics + * configuration of @dev. + * + * Since this initializes the modeset locks, no locking is possible. Which is no + * problem, since this should happen single threaded at init time. It is the + * driver's problem to ensure this guarantee. + * + */ +void drm_mode_config_init(struct drm_device *dev) +{ + mutex_init(&dev->mode_config.mutex); + mutex_init(&dev->mode_config.idr_mutex); + mutex_init(&dev->mode_config.fb_lock); + INIT_LIST_HEAD(&dev->mode_config.fb_list); + INIT_LIST_HEAD(&dev->mode_config.crtc_list); + INIT_LIST_HEAD(&dev->mode_config.connector_list); + INIT_LIST_HEAD(&dev->mode_config.encoder_list); + INIT_LIST_HEAD(&dev->mode_config.property_list); + INIT_LIST_HEAD(&dev->mode_config.property_blob_list); + INIT_LIST_HEAD(&dev->mode_config.plane_list); + idr_init(&dev->mode_config.crtc_idr); + + drm_modeset_lock_all(dev); + drm_mode_create_standard_connector_properties(dev); + drm_modeset_unlock_all(dev); + + /* Just to be sure */ + dev->mode_config.num_fb = 0; + dev->mode_config.num_connector = 0; + dev->mode_config.num_crtc = 0; + dev->mode_config.num_encoder = 0; +} +EXPORT_SYMBOL(drm_mode_config_init); + +/** + * drm_mode_config_cleanup - free up DRM mode_config info + * @dev: DRM device + * + * Free up all the connectors and CRTCs associated with this DRM device, then + * free up the framebuffers and associated buffer objects. + * + * Note that since this /should/ happen single-threaded at driver/device + * teardown time, no locking is required. It's the driver's job to ensure that + * this guarantee actually holds true. + * + * FIXME: cleanup any dangling user buffer objects too + */ +void drm_mode_config_cleanup(struct drm_device *dev) +{ + struct drm_connector *connector, *ot; + struct drm_crtc *crtc, *ct; + struct drm_encoder *encoder, *enct; + struct drm_framebuffer *fb, *fbt; + struct drm_property *property, *pt; + struct drm_property_blob *blob, *bt; + struct drm_plane *plane, *plt; + + list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, + head) { + encoder->funcs->destroy(encoder); + } + + list_for_each_entry_safe(connector, ot, + &dev->mode_config.connector_list, head) { + connector->funcs->destroy(connector); + } + + list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, + head) { + drm_property_destroy(dev, property); + } + + list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, + head) { + drm_property_destroy_blob(dev, blob); + } + + /* + * Single-threaded teardown context, so it's not required to grab the + * fb_lock to protect against concurrent fb_list access. Contrary, it + * would actually deadlock with the drm_framebuffer_cleanup function. + * + * Also, if there are any framebuffers left, that's a driver leak now, + * so politely WARN about this. + */ + WARN_ON(!list_empty(&dev->mode_config.fb_list)); + list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { + drm_framebuffer_remove(fb); + } + + list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, + head) { + plane->funcs->destroy(plane); + } + + list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { + crtc->funcs->destroy(crtc); + } + + idr_destroy(&dev->mode_config.crtc_idr); +} +EXPORT_SYMBOL(drm_mode_config_cleanup); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index be174ca..0ac1991 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -297,7 +297,6 @@ static void __exit drm_core_exit(void) unregister_chrdev(DRM_MAJOR, "drm"); - idr_remove_all(&drm_minors_idr); idr_destroy(&drm_minors_idr); } @@ -409,6 +408,7 @@ long drm_ioctl(struct file *filp, usize = asize = _IOC_SIZE(cmd); if (drv_size > asize) asize = drv_size; + cmd = ioctl->cmd_drv; } else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) { ioctl = &drm_ioctls[nr]; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c194f4e..e2acfdb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1634,7 +1634,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; - unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4; + unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4; unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); /* ignore tiny modes */ @@ -1715,6 +1715,7 @@ set_size: } mode->type = DRM_MODE_TYPE_DRIVER; + mode->vrefresh = drm_mode_vrefresh(mode); drm_mode_set_name(mode); return mode; diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 38d3943..fa445dd 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -31,10 +31,11 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob " "from built-in data or /lib/firmware instead. "); -#define GENERIC_EDIDS 4 +#define GENERIC_EDIDS 5 static char *generic_edid_name[GENERIC_EDIDS] = { "edid/1024x768.bin", "edid/1280x1024.bin", + "edid/1600x1200.bin", "edid/1680x1050.bin", "edid/1920x1080.bin", }; @@ -79,6 +80,24 @@ static u8 generic_edid[GENERIC_EDIDS][128] = { { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78, + 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, + 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f, + 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, + 0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, + 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, + 0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55, + 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d, + }, + { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78, 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 59d6b9b..6764dce 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1398,7 +1398,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) struct drm_mode_set *modeset; bool *enabled; int width, height; - int i, ret; + int i; DRM_DEBUG_KMS("\n"); @@ -1419,16 +1419,23 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) drm_enable_connectors(fb_helper, enabled); - ret = drm_target_cloned(fb_helper, modes, enabled, width, height); - if (!ret) { - ret = drm_target_preferred(fb_helper, modes, enabled, width, height); - if (!ret) + if (!(fb_helper->funcs->initial_config && + fb_helper->funcs->initial_config(fb_helper, crtcs, modes, + enabled, width, height))) { + memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0])); + memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0])); + + if (!drm_target_cloned(fb_helper, + modes, enabled, width, height) && + !drm_target_preferred(fb_helper, + modes, enabled, width, height)) DRM_ERROR("Unable to find initial modes\n"); - } - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); + DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", + width, height); - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + } /* need to set the modesets up here for use later */ /* fill out the connector<->crtc mappings into the modesets */ diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 13fdcd1..429e07d 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -123,6 +123,7 @@ int drm_open(struct inode *inode, struct file *filp) int retcode = 0; int need_setup = 0; struct address_space *old_mapping; + struct address_space *old_imapping; minor = idr_find(&drm_minors_idr, minor_id); if (!minor) @@ -137,6 +138,7 @@ int drm_open(struct inode *inode, struct file *filp) if (!dev->open_count++) need_setup = 1; mutex_lock(&dev->struct_mutex); + old_imapping = inode->i_mapping; old_mapping = dev->dev_mapping; if (old_mapping == NULL) dev->dev_mapping = &inode->i_data; @@ -159,8 +161,8 @@ int drm_open(struct inode *inode, struct file *filp) err_undo: mutex_lock(&dev->struct_mutex); - filp->f_mapping = old_mapping; - inode->i_mapping = old_mapping; + filp->f_mapping = old_imapping; + inode->i_mapping = old_imapping; iput(container_of(dev->dev_mapping, struct inode, i_data)); dev->dev_mapping = old_mapping; mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 24efae4..af779ae 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -270,21 +270,19 @@ drm_gem_handle_create(struct drm_file *file_priv, int ret; /* - * Get the user-visible handle using idr. + * Get the user-visible handle using idr. Preload and perform + * allocation under our spinlock. */ -again: - /* ensure there is space available to allocate a handle */ - if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0) - return -ENOMEM; - - /* do the allocation under our spinlock */ + idr_preload(GFP_KERNEL); spin_lock(&file_priv->table_lock); - ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep); + + ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT); + spin_unlock(&file_priv->table_lock); - if (ret == -EAGAIN) - goto again; - else if (ret) + idr_preload_end(); + if (ret < 0) return ret; + *handlep = ret; drm_gem_object_handle_reference(obj); @@ -451,29 +449,25 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, if (obj == NULL) return -ENOENT; -again: - if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) { - ret = -ENOMEM; - goto err; - } - + idr_preload(GFP_KERNEL); spin_lock(&dev->object_name_lock); if (!obj->name) { - ret = idr_get_new_above(&dev->object_name_idr, obj, 1, - &obj->name); + ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT); + obj->name = ret; args->name = (uint64_t) obj->name; spin_unlock(&dev->object_name_lock); + idr_preload_end(); - if (ret == -EAGAIN) - goto again; - else if (ret) + if (ret < 0) goto err; + ret = 0; /* Allocate a reference for the name table. */ drm_gem_object_reference(obj); } else { args->name = (uint64_t) obj->name; spin_unlock(&dev->object_name_lock); + idr_preload_end(); ret = 0; } @@ -561,8 +555,6 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private) { idr_for_each(&file_private->object_idr, &drm_gem_object_release_handle, file_private); - - idr_remove_all(&file_private->object_idr); idr_destroy(&file_private->object_idr); } diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index 8025454..7e4bae7 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -60,14 +60,13 @@ void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key) { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list; unsigned int hashed_key; int count = 0; hashed_key = hash_long(key, ht->order); DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); h_list = &ht->table[hashed_key]; - hlist_for_each_entry(entry, list, h_list, head) + hlist_for_each_entry(entry, h_list, head) DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); } @@ -76,14 +75,13 @@ static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list; unsigned int hashed_key; hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; - hlist_for_each_entry(entry, list, h_list, head) { + hlist_for_each_entry(entry, h_list, head) { if (entry->key == key) - return list; + return &entry->head; if (entry->key > key) break; } @@ -95,14 +93,13 @@ static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht, { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list; unsigned int hashed_key; hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, list, h_list, head) { + hlist_for_each_entry_rcu(entry, h_list, head) { if (entry->key == key) - return list; + return &entry->head; if (entry->key > key) break; } @@ -113,19 +110,19 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list, *parent; + struct hlist_node *parent; unsigned int hashed_key; unsigned long key = item->key; hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; parent = NULL; - hlist_for_each_entry(entry, list, h_list, head) { + hlist_for_each_entry(entry, h_list, head) { if (entry->key == key) return -EINVAL; if (entry->key > key) break; - parent = list; + parent = &entry->head; } if (parent) { hlist_add_after_rcu(parent, &item->head); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 366910d..25d0218 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -401,21 +401,17 @@ int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages) { struct sg_table *sg = NULL; - struct scatterlist *iter; - int i; int ret; sg = kmalloc(sizeof(struct sg_table), GFP_KERNEL); if (!sg) goto out; - ret = sg_alloc_table(sg, nr_pages, GFP_KERNEL); + ret = sg_alloc_table_from_pages(sg, pages, nr_pages, 0, + nr_pages << PAGE_SHIFT, GFP_KERNEL); if (ret) goto out; - for_each_sg(sg->sgl, iter, nr_pages, i) - sg_set_page(iter, pages[i], PAGE_SIZE, 0); - return sg; out: kfree(sg); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 200e104..7d30802 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -109,7 +109,6 @@ EXPORT_SYMBOL(drm_ut_debug_printk); static int drm_minor_get_id(struct drm_device *dev, int type) { - int new_id; int ret; int base = 0, limit = 63; @@ -121,25 +120,11 @@ static int drm_minor_get_id(struct drm_device *dev, int type) limit = base + 255; } -again: - if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Out of memory expanding drawable idr\n"); - return -ENOMEM; - } mutex_lock(&dev->struct_mutex); - ret = idr_get_new_above(&drm_minors_idr, NULL, - base, &new_id); + ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL); mutex_unlock(&dev->struct_mutex); - if (ret == -EAGAIN) - goto again; - else if (ret) - return ret; - if (new_id >= limit) { - idr_remove(&drm_minors_idr, new_id); - return -EINVAL; - } - return new_id; + return ret == -ENOSPC ? -EINVAL : ret; } struct drm_master *drm_master_create(struct drm_minor *minor) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 67a83e6..411f69b7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1785,11 +1785,9 @@ static int fimc_probe(struct platform_device *pdev) /* resource memory */ ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); - if (!ctx->regs) { - dev_err(dev, "failed to map registers.\n"); - return -ENXIO; - } + ctx->regs = devm_ioremap_resource(dev, ctx->regs_res); + if (IS_ERR(ctx->regs)) + return PTR_ERR(ctx->regs); /* resource irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 9537761..98cc147 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -38,11 +38,12 @@ /* position control register for hardware window 0, 2 ~ 4.*/ #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) -/* size control register for hardware window 0. */ -#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08) -/* alpha control register for hardware window 1 ~ 4. */ -#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16) -/* size control register for hardware window 1 ~ 4. */ +/* + * size control register for hardware windows 0 and alpha control register + * for hardware windows 1 ~ 4 + */ +#define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16) +/* size control register for hardware windows 1 ~ 2. */ #define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) @@ -50,9 +51,9 @@ #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) /* color key control register for hardware window 1 ~ 4. */ -#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8)) +#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8)) /* color key value register for hardware window 1 ~ 4. */ -#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8)) +#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8)) /* FIMD has totally five hardware windows. */ #define WINDOWS_NR 5 @@ -109,9 +110,9 @@ struct fimd_context { #ifdef CONFIG_OF static const struct of_device_id fimd_driver_dt_match[] = { - { .compatible = "samsung,exynos4-fimd", + { .compatible = "samsung,exynos4210-fimd", .data = &exynos4_fimd_driver_data }, - { .compatible = "samsung,exynos5-fimd", + { .compatible = "samsung,exynos5250-fimd", .data = &exynos5_fimd_driver_data }, {}, }; @@ -581,7 +582,7 @@ static void fimd_win_commit(struct device *dev, int zpos) if (win != 3 && win != 4) { u32 offset = VIDOSD_D(win); if (win == 0) - offset = VIDOSD_C_SIZE_W0; + offset = VIDOSD_C(win); val = win_data->ovl_width * win_data->ovl_height; writel(val, ctx->regs + offset); @@ -913,11 +914,9 @@ static int fimd_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!ctx->regs) { - dev_err(dev, "failed to map registers\n"); - return -ENXIO; - } + ctx->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctx->regs)) + return PTR_ERR(ctx->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 0fcfbe4..47a493c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -48,8 +48,14 @@ /* registers for base address */ #define G2D_SRC_BASE_ADDR 0x0304 +#define G2D_SRC_COLOR_MODE 0x030C +#define G2D_SRC_LEFT_TOP 0x0310 +#define G2D_SRC_RIGHT_BOTTOM 0x0314 #define G2D_SRC_PLANE2_BASE_ADDR 0x0318 #define G2D_DST_BASE_ADDR 0x0404 +#define G2D_DST_COLOR_MODE 0x040C +#define G2D_DST_LEFT_TOP 0x0410 +#define G2D_DST_RIGHT_BOTTOM 0x0414 #define G2D_DST_PLANE2_BASE_ADDR 0x0418 #define G2D_PAT_BASE_ADDR 0x0500 #define G2D_MSK_BASE_ADDR 0x0520 @@ -82,7 +88,7 @@ #define G2D_DMA_LIST_DONE_COUNT_OFFSET 17 /* G2D_DMA_HOLD_CMD */ -#define G2D_USET_HOLD (1 << 2) +#define G2D_USER_HOLD (1 << 2) #define G2D_LIST_HOLD (1 << 1) #define G2D_BITBLT_HOLD (1 << 0) @@ -91,13 +97,27 @@ #define G2D_START_NHOLT (1 << 1) #define G2D_START_BITBLT (1 << 0) +/* buffer color format */ +#define G2D_FMT_XRGB8888 0 +#define G2D_FMT_ARGB8888 1 +#define G2D_FMT_RGB565 2 +#define G2D_FMT_XRGB1555 3 +#define G2D_FMT_ARGB1555 4 +#define G2D_FMT_XRGB4444 5 +#define G2D_FMT_ARGB4444 6 +#define G2D_FMT_PACKED_RGB888 7 +#define G2D_FMT_A8 11 +#define G2D_FMT_L8 12 + +/* buffer valid length */ +#define G2D_LEN_MIN 1 +#define G2D_LEN_MAX 8000 + #define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) #define G2D_CMDLIST_NUM 64 #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) #define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) -#define MAX_BUF_ADDR_NR 6 - /* maximum buffer pool size of userptr is 64MB as default */ #define MAX_POOL (64 * 1024 * 1024) @@ -106,6 +126,17 @@ enum { BUF_TYPE_USERPTR, }; +enum g2d_reg_type { + REG_TYPE_NONE = -1, + REG_TYPE_SRC, + REG_TYPE_SRC_PLANE2, + REG_TYPE_DST, + REG_TYPE_DST_PLANE2, + REG_TYPE_PAT, + REG_TYPE_MSK, + MAX_REG_TYPE_NR +}; + /* cmdlist data structure */ struct g2d_cmdlist { u32 head; @@ -113,6 +144,42 @@ struct g2d_cmdlist { u32 last; /* last data offset */ }; +/* + * A structure of buffer description + * + * @format: color format + * @left_x: the x coordinates of left top corner + * @top_y: the y coordinates of left top corner + * @right_x: the x coordinates of right bottom corner + * @bottom_y: the y coordinates of right bottom corner + * + */ +struct g2d_buf_desc { + unsigned int format; + unsigned int left_x; + unsigned int top_y; + unsigned int right_x; + unsigned int bottom_y; +}; + +/* + * A structure of buffer information + * + * @map_nr: manages the number of mapped buffers + * @reg_types: stores regitster type in the order of requested command + * @handles: stores buffer handle in its reg_type position + * @types: stores buffer type in its reg_type position + * @descs: stores buffer description in its reg_type position + * + */ +struct g2d_buf_info { + unsigned int map_nr; + enum g2d_reg_type reg_types[MAX_REG_TYPE_NR]; + unsigned long handles[MAX_REG_TYPE_NR]; + unsigned int types[MAX_REG_TYPE_NR]; + struct g2d_buf_desc descs[MAX_REG_TYPE_NR]; +}; + struct drm_exynos_pending_g2d_event { struct drm_pending_event base; struct drm_exynos_g2d_event event; @@ -131,14 +198,11 @@ struct g2d_cmdlist_userptr { bool in_pool; bool out_of_list; }; - struct g2d_cmdlist_node { struct list_head list; struct g2d_cmdlist *cmdlist; - unsigned int map_nr; - unsigned long handles[MAX_BUF_ADDR_NR]; - unsigned int obj_type[MAX_BUF_ADDR_NR]; dma_addr_t dma_addr; + struct g2d_buf_info buf_info; struct drm_exynos_pending_g2d_event *event; }; @@ -188,6 +252,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) struct exynos_drm_subdrv *subdrv = &g2d->subdrv; int nr; int ret; + struct g2d_buf_info *buf_info; init_dma_attrs(&g2d->cmdlist_dma_attrs); dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs); @@ -209,11 +274,17 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) } for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { + unsigned int i; + node[nr].cmdlist = g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; node[nr].dma_addr = g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; + buf_info = &node[nr].buf_info; + for (i = 0; i < MAX_REG_TYPE_NR; i++) + buf_info->reg_types[i] = REG_TYPE_NONE; + list_add_tail(&node[nr].list, &g2d->free_cmdlist); } @@ -450,7 +521,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, DMA_BIDIRECTIONAL); if (ret < 0) { DRM_ERROR("failed to map sgt with dma region.\n"); - goto err_free_sgt; + goto err_sg_free_table; } g2d_userptr->dma_addr = sgt->sgl[0].dma_address; @@ -467,8 +538,10 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return &g2d_userptr->dma_addr; -err_free_sgt: +err_sg_free_table: sg_free_table(sgt); + +err_free_sgt: kfree(sgt); sgt = NULL; @@ -506,36 +579,172 @@ static void g2d_userptr_free_all(struct drm_device *drm_dev, g2d->current_pool = 0; } +static enum g2d_reg_type g2d_get_reg_type(int reg_offset) +{ + enum g2d_reg_type reg_type; + + switch (reg_offset) { + case G2D_SRC_BASE_ADDR: + case G2D_SRC_COLOR_MODE: + case G2D_SRC_LEFT_TOP: + case G2D_SRC_RIGHT_BOTTOM: + reg_type = REG_TYPE_SRC; + break; + case G2D_SRC_PLANE2_BASE_ADDR: + reg_type = REG_TYPE_SRC_PLANE2; + break; + case G2D_DST_BASE_ADDR: + case G2D_DST_COLOR_MODE: + case G2D_DST_LEFT_TOP: + case G2D_DST_RIGHT_BOTTOM: + reg_type = REG_TYPE_DST; + break; + case G2D_DST_PLANE2_BASE_ADDR: + reg_type = REG_TYPE_DST_PLANE2; + break; + case G2D_PAT_BASE_ADDR: + reg_type = REG_TYPE_PAT; + break; + case G2D_MSK_BASE_ADDR: + reg_type = REG_TYPE_MSK; + break; + default: + reg_type = REG_TYPE_NONE; + DRM_ERROR("Unknown register offset![%d]\n", reg_offset); + break; + }; + + return reg_type; +} + +static unsigned long g2d_get_buf_bpp(unsigned int format) +{ + unsigned long bpp; + + switch (format) { + case G2D_FMT_XRGB8888: + case G2D_FMT_ARGB8888: + bpp = 4; + break; + case G2D_FMT_RGB565: + case G2D_FMT_XRGB1555: + case G2D_FMT_ARGB1555: + case G2D_FMT_XRGB4444: + case G2D_FMT_ARGB4444: + bpp = 2; + break; + case G2D_FMT_PACKED_RGB888: + bpp = 3; + break; + default: + bpp = 1; + break; + } + + return bpp; +} + +static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, + enum g2d_reg_type reg_type, + unsigned long size) +{ + unsigned int width, height; + unsigned long area; + + /* + * check source and destination buffers only. + * so the others are always valid. + */ + if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST) + return true; + + width = buf_desc->right_x - buf_desc->left_x; + if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { + DRM_ERROR("width[%u] is out of range!\n", width); + return false; + } + + height = buf_desc->bottom_y - buf_desc->top_y; + if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { + DRM_ERROR("height[%u] is out of range!\n", height); + return false; + } + + area = (unsigned long)width * (unsigned long)height * + g2d_get_buf_bpp(buf_desc->format); + if (area > size) { + DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size); + return false; + } + + return true; +} + static int g2d_map_cmdlist_gem(struct g2d_data *g2d, struct g2d_cmdlist_node *node, struct drm_device *drm_dev, struct drm_file *file) { struct g2d_cmdlist *cmdlist = node->cmdlist; + struct g2d_buf_info *buf_info = &node->buf_info; int offset; + int ret; int i; - for (i = 0; i < node->map_nr; i++) { + for (i = 0; i < buf_info->map_nr; i++) { + struct g2d_buf_desc *buf_desc; + enum g2d_reg_type reg_type; + int reg_pos; unsigned long handle; dma_addr_t *addr; - offset = cmdlist->last - (i * 2 + 1); - handle = cmdlist->data[offset]; + reg_pos = cmdlist->last - 2 * (i + 1); + + offset = cmdlist->data[reg_pos]; + handle = cmdlist->data[reg_pos + 1]; + + reg_type = g2d_get_reg_type(offset); + if (reg_type == REG_TYPE_NONE) { + ret = -EFAULT; + goto err; + } + + buf_desc = &buf_info->descs[reg_type]; + + if (buf_info->types[reg_type] == BUF_TYPE_GEM) { + unsigned long size; + + size = exynos_drm_gem_get_size(drm_dev, handle, file); + if (!size) { + ret = -EFAULT; + goto err; + } + + if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, + size)) { + ret = -EFAULT; + goto err; + } - if (node->obj_type[i] == BUF_TYPE_GEM) { addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, file); if (IS_ERR(addr)) { - node->map_nr = i; - return -EFAULT; + ret = -EFAULT; + goto err; } } else { struct drm_exynos_g2d_userptr g2d_userptr; if (copy_from_user(&g2d_userptr, (void __user *)handle, sizeof(struct drm_exynos_g2d_userptr))) { - node->map_nr = i; - return -EFAULT; + ret = -EFAULT; + goto err; + } + + if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, + g2d_userptr.size)) { + ret = -EFAULT; + goto err; } addr = g2d_userptr_get_dma_addr(drm_dev, @@ -544,16 +753,21 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, file, &handle); if (IS_ERR(addr)) { - node->map_nr = i; - return -EFAULT; + ret = -EFAULT; + goto err; } } - cmdlist->data[offset] = *addr; - node->handles[i] = handle; + cmdlist->data[reg_pos + 1] = *addr; + buf_info->reg_types[i] = reg_type; + buf_info->handles[reg_type] = handle; } return 0; + +err: + buf_info->map_nr = i; + return ret; } static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, @@ -561,22 +775,33 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, struct drm_file *filp) { struct exynos_drm_subdrv *subdrv = &g2d->subdrv; + struct g2d_buf_info *buf_info = &node->buf_info; int i; - for (i = 0; i < node->map_nr; i++) { - unsigned long handle = node->handles[i]; + for (i = 0; i < buf_info->map_nr; i++) { + struct g2d_buf_desc *buf_desc; + enum g2d_reg_type reg_type; + unsigned long handle; + + reg_type = buf_info->reg_types[i]; + + buf_desc = &buf_info->descs[reg_type]; + handle = buf_info->handles[reg_type]; - if (node->obj_type[i] == BUF_TYPE_GEM) + if (buf_info->types[reg_type] == BUF_TYPE_GEM) exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle, filp); else g2d_userptr_put_dma_addr(subdrv->drm_dev, handle, false); - node->handles[i] = 0; + buf_info->reg_types[i] = REG_TYPE_NONE; + buf_info->handles[reg_type] = 0; + buf_info->types[reg_type] = 0; + memset(buf_desc, 0x00, sizeof(*buf_desc)); } - node->map_nr = 0; + buf_info->map_nr = 0; } static void g2d_dma_start(struct g2d_data *g2d, @@ -589,10 +814,6 @@ static void g2d_dma_start(struct g2d_data *g2d, pm_runtime_get_sync(g2d->dev); clk_enable(g2d->gate_clk); - /* interrupt enable */ - writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, - g2d->regs + G2D_INTEN); - writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR); writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND); } @@ -643,7 +864,6 @@ static void g2d_runqueue_worker(struct work_struct *work) struct g2d_data *g2d = container_of(work, struct g2d_data, runqueue_work); - mutex_lock(&g2d->runqueue_mutex); clk_disable(g2d->gate_clk); pm_runtime_put_sync(g2d->dev); @@ -724,20 +944,14 @@ static int g2d_check_reg_offset(struct device *dev, int i; for (i = 0; i < nr; i++) { - index = cmdlist->last - 2 * (i + 1); + struct g2d_buf_info *buf_info = &node->buf_info; + struct g2d_buf_desc *buf_desc; + enum g2d_reg_type reg_type; + unsigned long value; - if (for_addr) { - /* check userptr buffer type. */ - reg_offset = (cmdlist->data[index] & - ~0x7fffffff) >> 31; - if (reg_offset) { - node->obj_type[i] = BUF_TYPE_USERPTR; - cmdlist->data[index] &= ~G2D_BUF_USERPTR; - } - } + index = cmdlist->last - 2 * (i + 1); reg_offset = cmdlist->data[index] & ~0xfffff000; - if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END) goto err; if (reg_offset % 4) @@ -753,8 +967,60 @@ static int g2d_check_reg_offset(struct device *dev, if (!for_addr) goto err; - if (node->obj_type[i] != BUF_TYPE_USERPTR) - node->obj_type[i] = BUF_TYPE_GEM; + reg_type = g2d_get_reg_type(reg_offset); + if (reg_type == REG_TYPE_NONE) + goto err; + + /* check userptr buffer type. */ + if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { + buf_info->types[reg_type] = BUF_TYPE_USERPTR; + cmdlist->data[index] &= ~G2D_BUF_USERPTR; + } else + buf_info->types[reg_type] = BUF_TYPE_GEM; + break; + case G2D_SRC_COLOR_MODE: + case G2D_DST_COLOR_MODE: + if (for_addr) + goto err; + + reg_type = g2d_get_reg_type(reg_offset); + if (reg_type == REG_TYPE_NONE) + goto err; + + buf_desc = &buf_info->descs[reg_type]; + value = cmdlist->data[index + 1]; + + buf_desc->format = value & 0xf; + break; + case G2D_SRC_LEFT_TOP: + case G2D_DST_LEFT_TOP: + if (for_addr) + goto err; + + reg_type = g2d_get_reg_type(reg_offset); + if (reg_type == REG_TYPE_NONE) + goto err; + + buf_desc = &buf_info->descs[reg_type]; + value = cmdlist->data[index + 1]; + + buf_desc->left_x = value & 0x1fff; + buf_desc->top_y = (value & 0x1fff0000) >> 16; + break; + case G2D_SRC_RIGHT_BOTTOM: + case G2D_DST_RIGHT_BOTTOM: + if (for_addr) + goto err; + + reg_type = g2d_get_reg_type(reg_offset); + if (reg_type == REG_TYPE_NONE) + goto err; + + buf_desc = &buf_info->descs[reg_type]; + value = cmdlist->data[index + 1]; + + buf_desc->right_x = value & 0x1fff; + buf_desc->bottom_y = (value & 0x1fff0000) >> 16; break; default: if (for_addr) @@ -860,9 +1126,23 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR; cmdlist->data[cmdlist->last++] = 0; + /* + * 'LIST_HOLD' command should be set to the DMA_HOLD_CMD_REG + * and GCF bit should be set to INTEN register if user wants + * G2D interrupt event once current command list execution is + * finished. + * Otherwise only ACF bit should be set to INTEN register so + * that one interrupt is occured after all command lists + * have been completed. + */ if (node->event) { + cmdlist->data[cmdlist->last++] = G2D_INTEN; + cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF | G2D_INTEN_GCF; cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD; cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; + } else { + cmdlist->data[cmdlist->last++] = G2D_INTEN; + cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF; } /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ @@ -887,7 +1167,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, if (ret < 0) goto err_free_event; - node->map_nr = req->cmd_buf_nr; + node->buf_info.map_nr = req->cmd_buf_nr; if (req->cmd_buf_nr) { struct drm_exynos_g2d_cmd *cmd_buf; @@ -1137,10 +1417,9 @@ static int g2d_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - g2d->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!g2d->regs) { - dev_err(dev, "failed to remap I/O memory\n"); - ret = -ENXIO; + g2d->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(g2d->regs)) { + ret = PTR_ERR(g2d->regs); goto err_put_clk; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 67e17ce..0e6fe00 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -164,6 +164,27 @@ out: exynos_gem_obj = NULL; } +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(dev, file_priv, gem_handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return 0; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + drm_gem_object_unreference_unlocked(obj); + + return exynos_gem_obj->buffer->size; +} + + struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, unsigned long size) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 35ebac4..468766b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -130,6 +130,11 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* get buffer size to gem handle. */ +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv); + /* initialize gem object. */ int exynos_drm_gem_init_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 8140753..7841c3b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1692,11 +1692,9 @@ static int gsc_probe(struct platform_device *pdev) /* resource memory */ ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); - if (!ctx->regs) { - dev_err(dev, "failed to map registers.\n"); - return -ENXIO; - } + ctx->regs = devm_ioremap_resource(dev, ctx->regs_res); + if (IS_ERR(ctx->regs)) + return PTR_ERR(ctx->regs); /* resource irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 1a55635..1adce07 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -137,21 +137,15 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, DRM_DEBUG_KMS("%s\n", __func__); -again: - /* ensure there is space available to allocate a handle */ - if (idr_pre_get(id_idr, GFP_KERNEL) == 0) { - DRM_ERROR("failed to get idr.\n"); - return -ENOMEM; - } - /* do the allocation under our mutexlock */ mutex_lock(lock); - ret = idr_get_new_above(id_idr, obj, 1, (int *)idp); + ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL); mutex_unlock(lock); - if (ret == -EAGAIN) - goto again; + if (ret < 0) + return ret; - return ret; + *idp = ret; + return 0; } static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) @@ -1786,8 +1780,6 @@ err_iommu: drm_iommu_detach_device(drm_dev, ippdrv->dev); err_idr: - idr_remove_all(&ctx->ipp_idr); - idr_remove_all(&ctx->prop_idr); idr_destroy(&ctx->ipp_idr); idr_destroy(&ctx->prop_idr); return ret; @@ -1965,8 +1957,6 @@ static int ipp_remove(struct platform_device *pdev) exynos_drm_subdrv_unregister(&ctx->subdrv); /* remove,destroy ipp idr */ - idr_remove_all(&ctx->ipp_idr); - idr_remove_all(&ctx->prop_idr); idr_destroy(&ctx->ipp_idr); idr_destroy(&ctx->prop_idr); diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index f976e29..a40b9fb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -656,11 +656,9 @@ static int rotator_probe(struct platform_device *pdev) platform_get_device_id(pdev)->driver_data; rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rot->regs = devm_request_and_ioremap(dev, rot->regs_res); - if (!rot->regs) { - dev_err(dev, "failed to map register\n"); - return -ENXIO; - } + rot->regs = devm_ioremap_resource(dev, rot->regs_res); + if (IS_ERR(rot->regs)) + return PTR_ERR(rot->regs); rot->irq = platform_get_irq(pdev, 0); if (rot->irq < 0) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 13ccbd4..9504b0c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -117,13 +117,12 @@ static struct edid *vidi_get_edid(struct device *dev, } edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH; - edid = kzalloc(edid_len, GFP_KERNEL); + edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL); if (!edid) { DRM_DEBUG_KMS("failed to allocate edid\n"); return ERR_PTR(-ENOMEM); } - memcpy(edid, ctx->raw_edid, edid_len); return edid; } @@ -563,12 +562,11 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } edid_len = (1 + raw_edid->extensions) * EDID_LENGTH; - ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL); + ctx->raw_edid = kmemdup(raw_edid, edid_len, GFP_KERNEL); if (!ctx->raw_edid) { DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); return -ENOMEM; } - memcpy(ctx->raw_edid, raw_edid, edid_len); } else { /* * with connection = 0, free raw_edid diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 6d63f90..2c5f266 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -2226,11 +2226,9 @@ static int hdmi_probe(struct platform_device *pdev) return -ENOENT; } - hdata->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!hdata->regs) { - DRM_ERROR("failed to map registers\n"); - return -ENXIO; - } + hdata->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hdata->regs)) + return PTR_ERR(hdata->regs); ret = devm_gpio_request(&pdev->dev, hdata->hpd_gpio, "HPD"); if (ret) { diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e919aba..2f4f72f 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -818,7 +818,7 @@ static void mixer_win_disable(void *ctx, int win) mixer_ctx->win_data[win].enabled = false; } -int mixer_check_timing(void *ctx, struct fb_videomode *timing) +static int mixer_check_timing(void *ctx, struct fb_videomode *timing) { struct mixer_context *mixer_ctx = ctx; u32 w, h; diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index 92760ff..1f6e2df 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -1,6 +1,6 @@ config DRM_GMA500 tristate "Intel GMA5/600 KMS Framebuffer" - depends on DRM && PCI && X86 && EXPERIMENTAL + depends on DRM && PCI && X86 select FB_CFB_COPYAREA select FB_CFB_FILLRECT select FB_CFB_IMAGEBLIT diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 51044cc..88d9ef6 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -27,6 +27,7 @@ #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/module.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 01e3242..1f82183 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -205,7 +205,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt) WARN_ON(gt->pages); /* This is the shared memory object that backs the GEM resource */ - inode = gt->gem.filp->f_path.dentry->d_inode; + inode = file_inode(gt->gem.filp); mapping = inode->i_mapping; gt->pages = kmalloc(pages * sizeof(struct page *), GFP_KERNEL); diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7c65ab8..be88532 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -103,7 +103,7 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", + seq_printf(m, "%pK: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), @@ -772,6 +772,23 @@ static int i915_error_state(struct seq_file *m, void *unused) } } } + + obj = error->ring[i].ctx; + if (obj) { + seq_printf(m, "%s --- HW Context = 0x%08x\n", + dev_priv->ring[i].name, + obj->gtt_offset); + offset = 0; + for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { + seq_printf(m, "[%04x] %08x %08x %08x %08x\n", + offset, + obj->pages[0][elt], + obj->pages[0][elt+1], + obj->pages[0][elt+2], + obj->pages[0][elt+3]); + offset += 16; + } + } } if (error->overlay) @@ -849,76 +866,42 @@ static const struct file_operations i915_error_state_fops = { .release = i915_error_state_release, }; -static ssize_t -i915_next_seqno_read(struct file *filp, - char __user *ubuf, - size_t max, - loff_t *ppos) +static int +i915_next_seqno_get(void *data, u64 *val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; drm_i915_private_t *dev_priv = dev->dev_private; - char buf[80]; - int len; int ret; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; - len = snprintf(buf, sizeof(buf), - "next_seqno : 0x%x\n", - dev_priv->next_seqno); - + *val = dev_priv->next_seqno; mutex_unlock(&dev->struct_mutex); - if (len > sizeof(buf)) - len = sizeof(buf); - - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_next_seqno_write(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - struct drm_device *dev = filp->private_data; - char buf[20]; - u32 val = 1; +static int +i915_next_seqno_set(void *data, u64 val) +{ + struct drm_device *dev = data; int ret; - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; - - ret = kstrtouint(buf, 0, &val); - if (ret < 0) - return ret; - } - ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; ret = i915_gem_set_seqno(dev, val); - mutex_unlock(&dev->struct_mutex); - return ret ?: cnt; + return ret; } -static const struct file_operations i915_next_seqno_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_next_seqno_read, - .write = i915_next_seqno_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops, + i915_next_seqno_get, i915_next_seqno_set, + "next_seqno : 0x%llx\n"); static int i915_rstdby_delays(struct seq_file *m, void *unused) { @@ -1550,7 +1533,7 @@ static const char *swizzle_string(unsigned swizzle) case I915_BIT_6_SWIZZLE_9_10_17: return "bit9/bit10/bit17"; case I915_BIT_6_SWIZZLE_UNKNOWN: - return "unkown"; + return "unknown"; } return "bug"; @@ -1680,105 +1663,51 @@ static int i915_dpio_info(struct seq_file *m, void *data) return 0; } -static ssize_t -i915_wedged_read(struct file *filp, - char __user *ubuf, - size_t max, - loff_t *ppos) +static int +i915_wedged_get(void *data, u64 *val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; drm_i915_private_t *dev_priv = dev->dev_private; - char buf[80]; - int len; - - len = snprintf(buf, sizeof(buf), - "wedged : %d\n", - atomic_read(&dev_priv->gpu_error.reset_counter)); - if (len > sizeof(buf)) - len = sizeof(buf); + *val = atomic_read(&dev_priv->gpu_error.reset_counter); - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_wedged_write(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) +static int +i915_wedged_set(void *data, u64 val) { - struct drm_device *dev = filp->private_data; - char buf[20]; - int val = 1; - - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; + struct drm_device *dev = data; - val = simple_strtoul(buf, NULL, 0); - } - - DRM_INFO("Manually setting wedged to %d\n", val); + DRM_INFO("Manually setting wedged to %llu\n", val); i915_handle_error(dev, val); - return cnt; + return 0; } -static const struct file_operations i915_wedged_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_wedged_read, - .write = i915_wedged_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_wedged_fops, + i915_wedged_get, i915_wedged_set, + "wedged : %llu\n"); -static ssize_t -i915_ring_stop_read(struct file *filp, - char __user *ubuf, - size_t max, - loff_t *ppos) +static int +i915_ring_stop_get(void *data, u64 *val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; drm_i915_private_t *dev_priv = dev->dev_private; - char buf[20]; - int len; - - len = snprintf(buf, sizeof(buf), - "0x%08x\n", dev_priv->gpu_error.stop_rings); - if (len > sizeof(buf)) - len = sizeof(buf); + *val = dev_priv->gpu_error.stop_rings; - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_ring_stop_write(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) +static int +i915_ring_stop_set(void *data, u64 val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - char buf[20]; - int val = 0, ret; - - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; - - val = simple_strtoul(buf, NULL, 0); - } + int ret; - DRM_DEBUG_DRIVER("Stopping rings 0x%08x\n", val); + DRM_DEBUG_DRIVER("Stopping rings 0x%08llx\n", val); ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) @@ -1787,16 +1716,12 @@ i915_ring_stop_write(struct file *filp, dev_priv->gpu_error.stop_rings = val; mutex_unlock(&dev->struct_mutex); - return cnt; + return 0; } -static const struct file_operations i915_ring_stop_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_ring_stop_read, - .write = i915_ring_stop_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_ring_stop_fops, + i915_ring_stop_get, i915_ring_stop_set, + "0x%08llx\n"); #define DROP_UNBOUND 0x1 #define DROP_BOUND 0x2 @@ -1806,46 +1731,23 @@ static const struct file_operations i915_ring_stop_fops = { DROP_BOUND | \ DROP_RETIRE | \ DROP_ACTIVE) -static ssize_t -i915_drop_caches_read(struct file *filp, - char __user *ubuf, - size_t max, - loff_t *ppos) +static int +i915_drop_caches_get(void *data, u64 *val) { - char buf[20]; - int len; - - len = snprintf(buf, sizeof(buf), "0x%08x\n", DROP_ALL); - if (len > sizeof(buf)) - len = sizeof(buf); + *val = DROP_ALL; - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_drop_caches_write(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) +static int +i915_drop_caches_set(void *data, u64 val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj, *next; - char buf[20]; - int val = 0, ret; - - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; - - val = simple_strtoul(buf, NULL, 0); - } + int ret; - DRM_DEBUG_DRIVER("Dropping caches: 0x%08x\n", val); + DRM_DEBUG_DRIVER("Dropping caches: 0x%08llx\n", val); /* No need to check and wait for gpu resets, only libdrm auto-restarts * on ioctls on -EAGAIN. */ @@ -1883,27 +1785,19 @@ i915_drop_caches_write(struct file *filp, unlock: mutex_unlock(&dev->struct_mutex); - return ret ?: cnt; + return ret; } -static const struct file_operations i915_drop_caches_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_drop_caches_read, - .write = i915_drop_caches_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_drop_caches_fops, + i915_drop_caches_get, i915_drop_caches_set, + "0x%08llx\n"); -static ssize_t -i915_max_freq_read(struct file *filp, - char __user *ubuf, - size_t max, - loff_t *ppos) +static int +i915_max_freq_get(void *data, u64 *val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; drm_i915_private_t *dev_priv = dev->dev_private; - char buf[80]; - int len, ret; + int ret; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; @@ -1912,42 +1806,23 @@ i915_max_freq_read(struct file *filp, if (ret) return ret; - len = snprintf(buf, sizeof(buf), - "max freq: %d\n", dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER); + *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); - if (len > sizeof(buf)) - len = sizeof(buf); - - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_max_freq_write(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) +static int +i915_max_freq_set(void *data, u64 val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - char buf[20]; - int val = 1, ret; + int ret; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; - - val = simple_strtoul(buf, NULL, 0); - } - - DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val); + DRM_DEBUG_DRIVER("Manually setting max freq to %llu\n", val); ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); if (ret) @@ -1956,30 +1831,24 @@ i915_max_freq_write(struct file *filp, /* * Turbo will still be enabled, but won't go above the set value. */ - dev_priv->rps.max_delay = val / GT_FREQUENCY_MULTIPLIER; - - gen6_set_rps(dev, val / GT_FREQUENCY_MULTIPLIER); + do_div(val, GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.max_delay = val; + gen6_set_rps(dev, val); mutex_unlock(&dev_priv->rps.hw_lock); - return cnt; + return 0; } -static const struct file_operations i915_max_freq_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_max_freq_read, - .write = i915_max_freq_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_max_freq_fops, + i915_max_freq_get, i915_max_freq_set, + "max freq: %llu\n"); -static ssize_t -i915_min_freq_read(struct file *filp, char __user *ubuf, size_t max, - loff_t *ppos) +static int +i915_min_freq_get(void *data, u64 *val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; drm_i915_private_t *dev_priv = dev->dev_private; - char buf[80]; - int len, ret; + int ret; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; @@ -1988,40 +1857,23 @@ i915_min_freq_read(struct file *filp, char __user *ubuf, size_t max, if (ret) return ret; - len = snprintf(buf, sizeof(buf), - "min freq: %d\n", dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER); + *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); - if (len > sizeof(buf)) - len = sizeof(buf); - - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt, - loff_t *ppos) +static int +i915_min_freq_set(void *data, u64 val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - char buf[20]; - int val = 1, ret; + int ret; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; - - val = simple_strtoul(buf, NULL, 0); - } - - DRM_DEBUG_DRIVER("Manually setting min freq to %d\n", val); + DRM_DEBUG_DRIVER("Manually setting min freq to %llu\n", val); ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); if (ret) @@ -2030,33 +1882,25 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt, /* * Turbo will still be enabled, but won't go below the set value. */ - dev_priv->rps.min_delay = val / GT_FREQUENCY_MULTIPLIER; - - gen6_set_rps(dev, val / GT_FREQUENCY_MULTIPLIER); + do_div(val, GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.min_delay = val; + gen6_set_rps(dev, val); mutex_unlock(&dev_priv->rps.hw_lock); - return cnt; + return 0; } -static const struct file_operations i915_min_freq_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_min_freq_read, - .write = i915_min_freq_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_min_freq_fops, + i915_min_freq_get, i915_min_freq_set, + "min freq: %llu\n"); -static ssize_t -i915_cache_sharing_read(struct file *filp, - char __user *ubuf, - size_t max, - loff_t *ppos) +static int +i915_cache_sharing_get(void *data, u64 *val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; drm_i915_private_t *dev_priv = dev->dev_private; - char buf[80]; u32 snpcr; - int len, ret; + int ret; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; @@ -2068,46 +1912,25 @@ i915_cache_sharing_read(struct file *filp, snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); mutex_unlock(&dev_priv->dev->struct_mutex); - len = snprintf(buf, sizeof(buf), - "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >> - GEN6_MBC_SNPCR_SHIFT); + *val = (snpcr & GEN6_MBC_SNPCR_MASK) >> GEN6_MBC_SNPCR_SHIFT; - if (len > sizeof(buf)) - len = sizeof(buf); - - return simple_read_from_buffer(ubuf, max, ppos, buf, len); + return 0; } -static ssize_t -i915_cache_sharing_write(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) +static int +i915_cache_sharing_set(void *data, u64 val) { - struct drm_device *dev = filp->private_data; + struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - char buf[20]; u32 snpcr; - int val = 1; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; - if (cnt > 0) { - if (cnt > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = 0; - - val = simple_strtoul(buf, NULL, 0); - } - - if (val < 0 || val > 3) + if (val > 3) return -EINVAL; - DRM_DEBUG_DRIVER("Manually setting uncore sharing to %d\n", val); + DRM_DEBUG_DRIVER("Manually setting uncore sharing to %llu\n", val); /* Update the cache sharing policy here as well */ snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); @@ -2115,16 +1938,12 @@ i915_cache_sharing_write(struct file *filp, snpcr |= (val << GEN6_MBC_SNPCR_SHIFT); I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr); - return cnt; + return 0; } -static const struct file_operations i915_cache_sharing_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = i915_cache_sharing_read, - .write = i915_cache_sharing_write, - .llseek = default_llseek, -}; +DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops, + i915_cache_sharing_get, i915_cache_sharing_set, + "%llu\n"); /* As the drm_debugfs_init() routines are called before dev->dev_private is * allocated we need to hook into the minor for release. */ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 4fa6beb..4be58e3 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1453,6 +1453,22 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv) } /** + * intel_early_sanitize_regs - clean up BIOS state + * @dev: DRM device + * + * This function must be called before we do any I915_READ or I915_WRITE. Its + * purpose is to clean up any state left by the BIOS that may affect us when + * reading and/or writing registers. + */ +static void intel_early_sanitize_regs(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (IS_HASWELL(dev)) + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); +} + +/** * i915_driver_load - setup chip and create an initial config * @dev: DRM device * @flags: startup flags @@ -1542,6 +1558,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_gmch; } + intel_early_sanitize_regs(dev); + aperture_size = dev_priv->gtt.mappable_end; dev_priv->gtt.mappable = @@ -1612,14 +1630,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) mutex_init(&dev_priv->rps.hw_lock); mutex_init(&dev_priv->modeset_restore_lock); - if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) - dev_priv->num_pipe = 3; - else if (IS_MOBILE(dev) || !IS_GEN2(dev)) - dev_priv->num_pipe = 2; - else - dev_priv->num_pipe = 1; + dev_priv->num_plane = 1; + if (IS_VALLEYVIEW(dev)) + dev_priv->num_plane = 2; - ret = drm_vblank_init(dev, dev_priv->num_pipe); + ret = drm_vblank_init(dev, INTEL_INFO(dev)->num_pipes); if (ret) goto out_gem_unload; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index c5b8c81..3b4b9c0 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -121,9 +121,12 @@ MODULE_PARM_DESC(i915_enable_ppgtt, unsigned int i915_preliminary_hw_support __read_mostly = 0; module_param_named(preliminary_hw_support, i915_preliminary_hw_support, int, 0600); MODULE_PARM_DESC(preliminary_hw_support, - "Enable preliminary hardware support. " - "Enable Haswell and ValleyView Support. " - "(default: false)"); + "Enable preliminary hardware support. (default: false)"); + +int i915_disable_power_well __read_mostly = 0; +module_param_named(disable_power_well, i915_disable_power_well, int, 0600); +MODULE_PARM_DESC(disable_power_well, + "Disable the power well when possible (default: false)"); static struct drm_driver driver; extern int intel_agp_enabled; @@ -138,74 +141,74 @@ extern int intel_agp_enabled; .driver_data = (unsigned long) info } static const struct intel_device_info intel_i830_info = { - .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, + .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_845g_info = { - .gen = 2, + .gen = 2, .num_pipes = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i85x_info = { - .gen = 2, .is_i85x = 1, .is_mobile = 1, + .gen = 2, .is_i85x = 1, .is_mobile = 1, .num_pipes = 2, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i865g_info = { - .gen = 2, + .gen = 2, .num_pipes = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915g_info = { - .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, + .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915gm_info = { - .gen = 3, .is_mobile = 1, + .gen = 3, .is_mobile = 1, .num_pipes = 2, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, }; static const struct intel_device_info intel_i945g_info = { - .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, + .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i945gm_info = { - .gen = 3, .is_i945gm = 1, .is_mobile = 1, + .gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, }; static const struct intel_device_info intel_i965g_info = { - .gen = 4, .is_broadwater = 1, + .gen = 4, .is_broadwater = 1, .num_pipes = 2, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_i965gm_info = { - .gen = 4, .is_crestline = 1, + .gen = 4, .is_crestline = 1, .num_pipes = 2, .is_mobile = 1, .has_fbc = 1, .has_hotplug = 1, .has_overlay = 1, .supports_tv = 1, }; static const struct intel_device_info intel_g33_info = { - .gen = 3, .is_g33 = 1, + .gen = 3, .is_g33 = 1, .num_pipes = 2, .need_gfx_hws = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_g45_info = { - .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, + .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2, .has_pipe_cxsr = 1, .has_hotplug = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_gm45_info = { - .gen = 4, .is_g4x = 1, + .gen = 4, .is_g4x = 1, .num_pipes = 2, .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, .supports_tv = 1, @@ -213,26 +216,26 @@ static const struct intel_device_info intel_gm45_info = { }; static const struct intel_device_info intel_pineview_info = { - .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, + .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .num_pipes = 2, .need_gfx_hws = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_ironlake_d_info = { - .gen = 5, + .gen = 5, .num_pipes = 2, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_ironlake_m_info = { - .gen = 5, .is_mobile = 1, + .gen = 5, .is_mobile = 1, .num_pipes = 2, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_sandybridge_d_info = { - .gen = 6, + .gen = 6, .num_pipes = 2, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, @@ -241,7 +244,7 @@ static const struct intel_device_info intel_sandybridge_d_info = { }; static const struct intel_device_info intel_sandybridge_m_info = { - .gen = 6, .is_mobile = 1, + .gen = 6, .is_mobile = 1, .num_pipes = 2, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 1, .has_bsd_ring = 1, @@ -250,61 +253,49 @@ static const struct intel_device_info intel_sandybridge_m_info = { .has_force_wake = 1, }; +#define GEN7_FEATURES \ + .gen = 7, .num_pipes = 3, \ + .need_gfx_hws = 1, .has_hotplug = 1, \ + .has_bsd_ring = 1, \ + .has_blt_ring = 1, \ + .has_llc = 1, \ + .has_force_wake = 1 + static const struct intel_device_info intel_ivybridge_d_info = { - .is_ivybridge = 1, .gen = 7, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_bsd_ring = 1, - .has_blt_ring = 1, - .has_llc = 1, - .has_force_wake = 1, + GEN7_FEATURES, + .is_ivybridge = 1, }; static const struct intel_device_info intel_ivybridge_m_info = { - .is_ivybridge = 1, .gen = 7, .is_mobile = 1, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */ - .has_bsd_ring = 1, - .has_blt_ring = 1, - .has_llc = 1, - .has_force_wake = 1, + GEN7_FEATURES, + .is_ivybridge = 1, + .is_mobile = 1, }; static const struct intel_device_info intel_valleyview_m_info = { - .gen = 7, .is_mobile = 1, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_fbc = 0, - .has_bsd_ring = 1, - .has_blt_ring = 1, + GEN7_FEATURES, + .is_mobile = 1, + .num_pipes = 2, .is_valleyview = 1, .display_mmio_offset = VLV_DISPLAY_BASE, }; static const struct intel_device_info intel_valleyview_d_info = { - .gen = 7, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_fbc = 0, - .has_bsd_ring = 1, - .has_blt_ring = 1, + GEN7_FEATURES, + .num_pipes = 2, .is_valleyview = 1, .display_mmio_offset = VLV_DISPLAY_BASE, }; static const struct intel_device_info intel_haswell_d_info = { - .is_haswell = 1, .gen = 7, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_bsd_ring = 1, - .has_blt_ring = 1, - .has_llc = 1, - .has_force_wake = 1, + GEN7_FEATURES, + .is_haswell = 1, }; static const struct intel_device_info intel_haswell_m_info = { - .is_haswell = 1, .gen = 7, .is_mobile = 1, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_bsd_ring = 1, - .has_blt_ring = 1, - .has_llc = 1, - .has_force_wake = 1, + GEN7_FEATURES, + .is_haswell = 1, + .is_mobile = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ @@ -379,16 +370,19 @@ static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x0A06, &intel_haswell_m_info), /* ULT GT1 mobile */ INTEL_VGA_DEVICE(0x0A16, &intel_haswell_m_info), /* ULT GT2 mobile */ INTEL_VGA_DEVICE(0x0A26, &intel_haswell_m_info), /* ULT GT2 mobile */ - INTEL_VGA_DEVICE(0x0D12, &intel_haswell_d_info), /* CRW GT1 desktop */ + INTEL_VGA_DEVICE(0x0D02, &intel_haswell_d_info), /* CRW GT1 desktop */ + INTEL_VGA_DEVICE(0x0D12, &intel_haswell_d_info), /* CRW GT2 desktop */ INTEL_VGA_DEVICE(0x0D22, &intel_haswell_d_info), /* CRW GT2 desktop */ - INTEL_VGA_DEVICE(0x0D32, &intel_haswell_d_info), /* CRW GT2 desktop */ - INTEL_VGA_DEVICE(0x0D1A, &intel_haswell_d_info), /* CRW GT1 server */ + INTEL_VGA_DEVICE(0x0D0A, &intel_haswell_d_info), /* CRW GT1 server */ + INTEL_VGA_DEVICE(0x0D1A, &intel_haswell_d_info), /* CRW GT2 server */ INTEL_VGA_DEVICE(0x0D2A, &intel_haswell_d_info), /* CRW GT2 server */ - INTEL_VGA_DEVICE(0x0D3A, &intel_haswell_d_info), /* CRW GT2 server */ - INTEL_VGA_DEVICE(0x0D16, &intel_haswell_m_info), /* CRW GT1 mobile */ + INTEL_VGA_DEVICE(0x0D06, &intel_haswell_m_info), /* CRW GT1 mobile */ + INTEL_VGA_DEVICE(0x0D16, &intel_haswell_m_info), /* CRW GT2 mobile */ INTEL_VGA_DEVICE(0x0D26, &intel_haswell_m_info), /* CRW GT2 mobile */ - INTEL_VGA_DEVICE(0x0D36, &intel_haswell_m_info), /* CRW GT2 mobile */ INTEL_VGA_DEVICE(0x0f30, &intel_valleyview_m_info), + INTEL_VGA_DEVICE(0x0f31, &intel_valleyview_m_info), + INTEL_VGA_DEVICE(0x0f32, &intel_valleyview_m_info), + INTEL_VGA_DEVICE(0x0f33, &intel_valleyview_m_info), INTEL_VGA_DEVICE(0x0157, &intel_valleyview_m_info), INTEL_VGA_DEVICE(0x0155, &intel_valleyview_d_info), {0, 0, 0} @@ -469,6 +463,7 @@ bool i915_semaphore_is_enabled(struct drm_device *dev) static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; /* ignore lid events during suspend */ mutex_lock(&dev_priv->modeset_restore_lock); @@ -492,9 +487,14 @@ static int i915_drm_freeze(struct drm_device *dev) cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work); - intel_modeset_disable(dev); - drm_irq_uninstall(dev); + dev_priv->enable_hotplug_processing = false; + /* + * Disable CRTCs directly since we want to preserve sw state + * for _thaw. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + dev_priv->display.crtc_disable(crtc); } i915_save_state(dev); @@ -550,6 +550,24 @@ void intel_console_resume(struct work_struct *work) console_unlock(); } +static void intel_resume_hotplug(struct drm_device *dev) +{ + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *encoder; + + mutex_lock(&mode_config->mutex); + DRM_DEBUG_KMS("running encoder hotplug functions\n"); + + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) + if (encoder->hot_plug) + encoder->hot_plug(encoder); + + mutex_unlock(&mode_config->mutex); + + /* Just fire off a uevent and let userspace tell us what to do */ + drm_helper_hpd_irq_event(dev); +} + static int __i915_drm_thaw(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -568,10 +586,25 @@ static int __i915_drm_thaw(struct drm_device *dev) error = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); - intel_modeset_init_hw(dev); - intel_modeset_setup_hw_state(dev, false); + /* We need working interrupts for modeset enabling ... */ drm_irq_install(dev); + + intel_modeset_init_hw(dev); + + drm_modeset_lock_all(dev); + intel_modeset_setup_hw_state(dev, true); + drm_modeset_unlock_all(dev); + + /* + * ... but also need to make sure that hotplug processing + * doesn't cause havoc. Like in the driver load code we don't + * bother with the tiny race here where we might loose hotplug + * notifications. + * */ intel_hpd_init(dev); + dev_priv->enable_hotplug_processing = true; + /* Config may have changed between suspend and resume */ + intel_resume_hotplug(dev); } intel_opregion_init(dev); @@ -716,6 +749,7 @@ static int ironlake_do_reset(struct drm_device *dev) int ret; gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + gdrst &= ~GRDOM_MASK; I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE); ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); @@ -724,6 +758,7 @@ static int ironlake_do_reset(struct drm_device *dev) /* We can't reset render&media without also resetting display ... */ gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + gdrst &= ~GRDOM_MASK; I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE); return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); @@ -787,7 +822,7 @@ int intel_gpu_reset(struct drm_device *dev) /* Also reset the gpu hangman. */ if (dev_priv->gpu_error.stop_rings) { - DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); + DRM_INFO("Simulated gpu hang, resetting stop_rings\n"); dev_priv->gpu_error.stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " @@ -1131,6 +1166,27 @@ ilk_dummy_write(struct drm_i915_private *dev_priv) I915_WRITE_NOTRACE(MI_MODE, 0); } +static void +hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg) +{ + if (IS_HASWELL(dev_priv->dev) && + (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { + DRM_ERROR("Unknown unclaimed register before writing to %x\n", + reg); + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); + } +} + +static void +hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg) +{ + if (IS_HASWELL(dev_priv->dev) && + (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { + DRM_ERROR("Unclaimed write to %x\n", reg); + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); + } +} + #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ u##x val = 0; \ @@ -1167,18 +1223,12 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ } \ if (IS_GEN5(dev_priv->dev)) \ ilk_dummy_write(dev_priv); \ - if (IS_HASWELL(dev_priv->dev) && (I915_READ_NOTRACE(GEN7_ERR_INT) & ERR_INT_MMIO_UNCLAIMED)) { \ - DRM_ERROR("Unknown unclaimed register before writing to %x\n", reg); \ - I915_WRITE_NOTRACE(GEN7_ERR_INT, ERR_INT_MMIO_UNCLAIMED); \ - } \ + hsw_unclaimed_reg_clear(dev_priv, reg); \ write##y(val, dev_priv->regs + reg); \ if (unlikely(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ - if (IS_HASWELL(dev_priv->dev) && (I915_READ_NOTRACE(GEN7_ERR_INT) & ERR_INT_MMIO_UNCLAIMED)) { \ - DRM_ERROR("Unclaimed write to %x\n", reg); \ - writel(ERR_INT_MMIO_UNCLAIMED, dev_priv->regs + GEN7_ERR_INT); \ - } \ + hsw_unclaimed_reg_check(dev_priv, reg); \ } __i915_write(8, b) __i915_write(16, w) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e95337c..44fca0b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -86,6 +86,19 @@ enum port { }; #define port_name(p) ((p) + 'A') +enum hpd_pin { + HPD_NONE = 0, + HPD_PORT_A = HPD_NONE, /* PORT_A is internal */ + HPD_TV = HPD_NONE, /* TV is known to be unreliable */ + HPD_CRT, + HPD_SDVO_B, + HPD_SDVO_C, + HPD_PORT_B, + HPD_PORT_C, + HPD_PORT_D, + HPD_NUM_PINS +}; + #define I915_GEM_GPU_DOMAINS \ (I915_GEM_DOMAIN_RENDER | \ I915_GEM_DOMAIN_SAMPLER | \ @@ -93,7 +106,7 @@ enum port { I915_GEM_DOMAIN_INSTRUCTION | \ I915_GEM_DOMAIN_VERTEX) -#define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) +#define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++) #define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \ list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \ @@ -243,7 +256,7 @@ struct drm_i915_error_state { int page_count; u32 gtt_offset; u32 *pages[0]; - } *ringbuffer, *batchbuffer; + } *ringbuffer, *batchbuffer, *ctx; struct drm_i915_error_request { long jiffies; u32 seqno; @@ -271,6 +284,9 @@ struct drm_i915_error_state { struct intel_display_error_state *display; }; +struct intel_crtc_config; +struct intel_crtc; + struct drm_i915_display_funcs { bool (*fbc_enabled)(struct drm_device *dev); void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval); @@ -283,9 +299,11 @@ struct drm_i915_display_funcs { void (*update_linetime_wm)(struct drm_device *dev, int pipe, struct drm_display_mode *mode); void (*modeset_global_resources)(struct drm_device *dev); + /* Returns the active state of the crtc, and if the crtc is active, + * fills out the pipe-config with the hw state. */ + bool (*get_pipe_config)(struct intel_crtc *, + struct intel_crtc_config *); int (*crtc_mode_set)(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); void (*crtc_enable)(struct drm_crtc *crtc); @@ -341,6 +359,7 @@ struct drm_i915_gt_funcs { struct intel_device_info { u32 display_mmio_offset; + u8 num_pipes:3; u8 gen; u8 is_mobile:1; u8 is_i85x:1; @@ -905,16 +924,14 @@ typedef struct drm_i915_private { struct mutex dpio_lock; /** Cached value of IMR to avoid reads in updating the bitfield */ - u32 pipestat[2]; u32 irq_mask; u32 gt_irq_mask; - u32 hotplug_supported_mask; struct work_struct hotplug_work; bool enable_hotplug_processing; - int num_pipe; int num_pch_pll; + int num_plane; unsigned long cfb_size; unsigned int cfb_fb; @@ -928,9 +945,14 @@ typedef struct drm_i915_private { struct intel_overlay *overlay; unsigned int sprite_scaling_enabled; + /* backlight */ + struct { + int level; + bool enabled; + struct backlight_device *device; + } backlight; + /* LVDS info */ - int backlight_level; /* restore backlight to this value */ - bool backlight_enabled; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ @@ -1032,8 +1054,6 @@ typedef struct drm_i915_private { */ struct work_struct console_resume_work; - struct backlight_device *backlight; - struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; @@ -1340,6 +1360,7 @@ struct drm_i915_file_private { #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) #define HAS_DDI(dev) (IS_HASWELL(dev)) +#define HAS_POWER_WELL(dev) (IS_HASWELL(dev)) #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 @@ -1398,6 +1419,7 @@ extern int i915_enable_fbc __read_mostly; extern bool i915_enable_hangcheck __read_mostly; extern int i915_enable_ppgtt __read_mostly; extern unsigned int i915_preliminary_hw_support __read_mostly; +extern int i915_disable_power_well __read_mostly; extern int i915_suspend(struct drm_device *dev, pm_message_t state); extern int i915_resume(struct drm_device *dev); @@ -1528,17 +1550,12 @@ void i915_gem_lastclose(struct drm_device *dev); int __must_check i915_gem_object_get_pages(struct drm_i915_gem_object *obj); static inline struct page *i915_gem_object_get_page(struct drm_i915_gem_object *obj, int n) { - struct scatterlist *sg = obj->pages->sgl; - int nents = obj->pages->nents; - while (nents > SG_MAX_SINGLE_ALLOC) { - if (n < SG_MAX_SINGLE_ALLOC - 1) - break; - - sg = sg_chain_ptr(sg + SG_MAX_SINGLE_ALLOC - 1); - n -= SG_MAX_SINGLE_ALLOC - 1; - nents -= SG_MAX_SINGLE_ALLOC - 1; - } - return sg_page(sg+n); + struct sg_page_iter sg_iter; + + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, n) + return sg_page_iter_page(&sg_iter); + + return NULL; } static inline void i915_gem_object_pin_pages(struct drm_i915_gem_object *obj) { @@ -1717,6 +1734,11 @@ void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); struct drm_i915_gem_object * i915_gem_object_create_stolen(struct drm_device *dev, u32 size); +struct drm_i915_gem_object * +i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, + u32 stolen_offset, + u32 gtt_offset, + u32 size); void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ @@ -1847,6 +1869,8 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv); int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val); int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); +int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); +int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg); @@ -1900,4 +1924,9 @@ static inline uint32_t i915_vgacntrl_reg(struct drm_device *dev) return VGACNTRL; } +static inline void __user *to_user_ptr(u64 address) +{ + return (void __user *)(uintptr_t)address; +} + #endif diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8413ffc..911bd40 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -411,10 +411,9 @@ i915_gem_shmem_pread(struct drm_device *dev, int obj_do_bit17_swizzling, page_do_bit17_swizzling; int prefaulted = 0; int needs_clflush = 0; - struct scatterlist *sg; - int i; + struct sg_page_iter sg_iter; - user_data = (char __user *) (uintptr_t) args->data_ptr; + user_data = to_user_ptr(args->data_ptr); remain = args->size; obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); @@ -441,11 +440,9 @@ i915_gem_shmem_pread(struct drm_device *dev, offset = args->offset; - for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) { - struct page *page; - - if (i < offset >> PAGE_SHIFT) - continue; + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, + offset >> PAGE_SHIFT) { + struct page *page = sg_page_iter_page(&sg_iter); if (remain <= 0) break; @@ -460,7 +457,6 @@ i915_gem_shmem_pread(struct drm_device *dev, if ((shmem_page_offset + page_length) > PAGE_SIZE) page_length = PAGE_SIZE - shmem_page_offset; - page = sg_page(sg); page_do_bit17_swizzling = obj_do_bit17_swizzling && (page_to_phys(page) & (1 << 17)) != 0; @@ -522,7 +518,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, return 0; if (!access_ok(VERIFY_WRITE, - (char __user *)(uintptr_t)args->data_ptr, + to_user_ptr(args->data_ptr), args->size)) return -EFAULT; @@ -613,7 +609,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, if (ret) goto out_unpin; - user_data = (char __user *) (uintptr_t) args->data_ptr; + user_data = to_user_ptr(args->data_ptr); remain = args->size; offset = obj->gtt_offset + args->offset; @@ -732,10 +728,9 @@ i915_gem_shmem_pwrite(struct drm_device *dev, int hit_slowpath = 0; int needs_clflush_after = 0; int needs_clflush_before = 0; - int i; - struct scatterlist *sg; + struct sg_page_iter sg_iter; - user_data = (char __user *) (uintptr_t) args->data_ptr; + user_data = to_user_ptr(args->data_ptr); remain = args->size; obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); @@ -768,13 +763,11 @@ i915_gem_shmem_pwrite(struct drm_device *dev, offset = args->offset; obj->dirty = 1; - for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) { - struct page *page; + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, + offset >> PAGE_SHIFT) { + struct page *page = sg_page_iter_page(&sg_iter); int partial_cacheline_write; - if (i < offset >> PAGE_SHIFT) - continue; - if (remain <= 0) break; @@ -796,7 +789,6 @@ i915_gem_shmem_pwrite(struct drm_device *dev, ((shmem_page_offset | page_length) & (boot_cpu_data.x86_clflush_size - 1)); - page = sg_page(sg); page_do_bit17_swizzling = obj_do_bit17_swizzling && (page_to_phys(page) & (1 << 17)) != 0; @@ -867,11 +859,11 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, return 0; if (!access_ok(VERIFY_READ, - (char __user *)(uintptr_t)args->data_ptr, + to_user_ptr(args->data_ptr), args->size)) return -EFAULT; - ret = fault_in_multipages_readable((char __user *)(uintptr_t)args->data_ptr, + ret = fault_in_multipages_readable(to_user_ptr(args->data_ptr), args->size); if (ret) return -EFAULT; @@ -1618,7 +1610,7 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) * To do this we must instruct the shmfs to drop all of its * backing pages, *now*. */ - inode = obj->base.filp->f_path.dentry->d_inode; + inode = file_inode(obj->base.filp); shmem_truncate_range(inode, 0, (loff_t)-1); obj->madv = __I915_MADV_PURGED; @@ -1633,9 +1625,8 @@ i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) static void i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) { - int page_count = obj->base.size / PAGE_SIZE; - struct scatterlist *sg; - int ret, i; + struct sg_page_iter sg_iter; + int ret; BUG_ON(obj->madv == __I915_MADV_PURGED); @@ -1655,8 +1646,8 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) if (obj->madv == I915_MADV_DONTNEED) obj->dirty = 0; - for_each_sg(obj->pages->sgl, sg, page_count, i) { - struct page *page = sg_page(sg); + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) { + struct page *page = sg_page_iter_page(&sg_iter); if (obj->dirty) set_page_dirty(page); @@ -1757,7 +1748,9 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) struct address_space *mapping; struct sg_table *st; struct scatterlist *sg; + struct sg_page_iter sg_iter; struct page *page; + unsigned long last_pfn = 0; /* suppress gcc warning */ gfp_t gfp; /* Assert that the object is not currently in any GPU domain. As it @@ -1783,11 +1776,13 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) * * Fail silently without starting the shrinker */ - mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; + mapping = file_inode(obj->base.filp)->i_mapping; gfp = mapping_gfp_mask(mapping); gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; gfp &= ~(__GFP_IO | __GFP_WAIT); - for_each_sg(st->sgl, sg, page_count, i) { + sg = st->sgl; + st->nents = 0; + for (i = 0; i < page_count; i++) { page = shmem_read_mapping_page_gfp(mapping, i, gfp); if (IS_ERR(page)) { i915_gem_purge(dev_priv, page_count); @@ -1810,9 +1805,18 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) gfp &= ~(__GFP_IO | __GFP_WAIT); } - sg_set_page(sg, page, PAGE_SIZE, 0); + if (!i || page_to_pfn(page) != last_pfn + 1) { + if (i) + sg = sg_next(sg); + st->nents++; + sg_set_page(sg, page, PAGE_SIZE, 0); + } else { + sg->length += PAGE_SIZE; + } + last_pfn = page_to_pfn(page); } + sg_mark_end(sg); obj->pages = st; if (i915_gem_object_needs_bit17_swizzle(obj)) @@ -1821,8 +1825,9 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) return 0; err_pages: - for_each_sg(st->sgl, sg, i, page_count) - page_cache_release(sg_page(sg)); + sg_mark_end(sg); + for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) + page_cache_release(sg_page_iter_page(&sg_iter)); sg_free_table(st); kfree(st); return PTR_ERR(page); @@ -2123,11 +2128,11 @@ static void i915_gem_reset_fences(struct drm_device *dev) for (i = 0; i < dev_priv->num_fence_regs; i++) { struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i]; - i915_gem_write_fence(dev, i, NULL); - if (reg->obj) i915_gem_object_fence_lost(reg->obj); + i915_gem_write_fence(dev, i, NULL); + reg->pin_count = 0; reg->obj = NULL; INIT_LIST_HEAD(®->lru_list); @@ -2717,6 +2722,7 @@ int i915_gem_object_put_fence(struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + struct drm_i915_fence_reg *fence; int ret; ret = i915_gem_object_wait_fence(obj); @@ -2726,10 +2732,10 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj) if (obj->fence_reg == I915_FENCE_REG_NONE) return 0; - i915_gem_object_update_fence(obj, - &dev_priv->fence_regs[obj->fence_reg], - false); + fence = &dev_priv->fence_regs[obj->fence_reg]; + i915_gem_object_fence_lost(obj); + i915_gem_object_update_fence(obj, fence, false); return 0; } @@ -3747,7 +3753,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, mask |= __GFP_DMA32; } - mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; + mapping = file_inode(obj->base.filp)->i_mapping; mapping_set_gfp_mask(mapping, mask); i915_gem_object_init(obj, &i915_gem_object_ops); @@ -4010,7 +4016,16 @@ int i915_gem_init(struct drm_device *dev) int ret; mutex_lock(&dev->struct_mutex); + + if (IS_VALLEYVIEW(dev)) { + /* VLVA0 (potential hack), BIOS isn't actually waking us */ + I915_WRITE(VLV_GTLC_WAKE_CTRL, 1); + if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) & 1) == 1, 10)) + DRM_DEBUG_DRIVER("allow wake ack timed out\n"); + } + i915_gem_init_global_gtt(dev); + ret = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); if (ret) { @@ -4232,7 +4247,7 @@ void i915_gem_free_all_phys_object(struct drm_device *dev) void i915_gem_detach_phys_object(struct drm_device *dev, struct drm_i915_gem_object *obj) { - struct address_space *mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; + struct address_space *mapping = file_inode(obj->base.filp)->i_mapping; char *vaddr; int i; int page_count; @@ -4268,7 +4283,7 @@ i915_gem_attach_phys_object(struct drm_device *dev, int id, int align) { - struct address_space *mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; + struct address_space *mapping = file_inode(obj->base.filp)->i_mapping; drm_i915_private_t *dev_priv = dev->dev_private; int ret = 0; int page_count; @@ -4327,7 +4342,7 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_file *file_priv) { void *vaddr = obj->phys_obj->handle->vaddr + args->offset; - char __user *user_data = (char __user *) (uintptr_t) args->data_ptr; + char __user *user_data = to_user_ptr(args->data_ptr); if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) { unsigned long unwritten; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 21177d9..94d873a 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -139,7 +139,7 @@ create_hw_context(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; struct i915_hw_context *ctx; - int ret, id; + int ret; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (ctx == NULL) @@ -164,22 +164,11 @@ create_hw_context(struct drm_device *dev, ctx->file_priv = file_priv; -again: - if (idr_pre_get(&file_priv->context_idr, GFP_KERNEL) == 0) { - ret = -ENOMEM; - DRM_DEBUG_DRIVER("idr allocation failed\n"); - goto err_out; - } - - ret = idr_get_new_above(&file_priv->context_idr, ctx, - DEFAULT_CONTEXT_ID + 1, &id); - if (ret == 0) - ctx->id = id; - - if (ret == -EAGAIN) - goto again; - else if (ret) + ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0, + GFP_KERNEL); + if (ret < 0) goto err_out; + ctx->id = ret; return ctx; diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 6a5af68..c6dfc14 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -62,7 +62,7 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme src = obj->pages->sgl; dst = st->sgl; for (i = 0; i < obj->pages->nents; i++) { - sg_set_page(dst, sg_page(src), PAGE_SIZE, 0); + sg_set_page(dst, sg_page(src), src->length, 0); dst = sg_next(dst); src = sg_next(src); } @@ -105,7 +105,7 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) { struct drm_i915_gem_object *obj = dma_buf->priv; struct drm_device *dev = obj->base.dev; - struct scatterlist *sg; + struct sg_page_iter sg_iter; struct page **pages; int ret, i; @@ -124,14 +124,15 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) ret = -ENOMEM; - pages = drm_malloc_ab(obj->pages->nents, sizeof(struct page *)); + pages = drm_malloc_ab(obj->base.size >> PAGE_SHIFT, sizeof(*pages)); if (pages == NULL) goto error; - for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) - pages[i] = sg_page(sg); + i = 0; + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0); + pages[i++] = sg_page_iter_page(&sg_iter); - obj->dma_buf_vmapping = vmap(pages, obj->pages->nents, 0, PAGE_KERNEL); + obj->dma_buf_vmapping = vmap(pages, i, 0, PAGE_KERNEL); drm_free_large(pages); if (!obj->dma_buf_vmapping) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 2f2daeb..117ce38 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -57,7 +57,7 @@ eb_create(struct drm_i915_gem_execbuffer2 *args) if (eb == NULL) { int size = args->buffer_count; int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; - BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); + BUILD_BUG_ON_NOT_POWER_OF_2(PAGE_SIZE / sizeof(struct hlist_head)); while (count > 2*size) count >>= 1; eb = kzalloc(count*sizeof(struct hlist_head) + @@ -305,7 +305,7 @@ i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj, struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; int remain, ret; - user_relocs = (void __user *)(uintptr_t)entry->relocs_ptr; + user_relocs = to_user_ptr(entry->relocs_ptr); remain = entry->relocation_count; while (remain) { @@ -359,8 +359,7 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, } static int -i915_gem_execbuffer_relocate(struct drm_device *dev, - struct eb_objects *eb) +i915_gem_execbuffer_relocate(struct eb_objects *eb) { struct drm_i915_gem_object *obj; int ret = 0; @@ -475,7 +474,6 @@ i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, - struct drm_file *file, struct list_head *objects, bool *need_relocs) { @@ -618,7 +616,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, u64 invalid_offset = (u64)-1; int j; - user_relocs = (void __user *)(uintptr_t)exec[i].relocs_ptr; + user_relocs = to_user_ptr(exec[i].relocs_ptr); if (copy_from_user(reloc+total, user_relocs, exec[i].relocation_count * sizeof(*reloc))) { @@ -663,7 +661,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, goto err; need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; - ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); + ret = i915_gem_execbuffer_reserve(ring, &eb->objects, &need_relocs); if (ret) goto err; @@ -732,22 +730,31 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, int count) { int i; + int relocs_total = 0; + int relocs_max = INT_MAX / sizeof(struct drm_i915_gem_relocation_entry); for (i = 0; i < count; i++) { - char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr; + char __user *ptr = to_user_ptr(exec[i].relocs_ptr); int length; /* limited by fault_in_pages_readable() */ if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS) return -EINVAL; - /* First check for malicious input causing overflow */ - if (exec[i].relocation_count > - INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) + /* First check for malicious input causing overflow in + * the worst case where we need to allocate the entire + * relocation tree as a single array. + */ + if (exec[i].relocation_count > relocs_max - relocs_total) return -EINVAL; + relocs_total += exec[i].relocation_count; length = exec[i].relocation_count * sizeof(struct drm_i915_gem_relocation_entry); - /* we may also need to update the presumed offsets */ + /* + * We must check that the entire relocation array is safe + * to read, but since we may need to update the presumed + * offsets during execution, check for full write access. + */ if (!access_ok(VERIFY_WRITE, ptr, length)) return -EFAULT; @@ -944,9 +951,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } if (copy_from_user(cliprects, - (struct drm_clip_rect __user *)(uintptr_t) - args->cliprects_ptr, - sizeof(*cliprects)*args->num_cliprects)) { + to_user_ptr(args->cliprects_ptr), + sizeof(*cliprects)*args->num_cliprects)) { ret = -EFAULT; goto pre_mutex_err; } @@ -981,13 +987,13 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, /* Move the objects en-masse into the GTT, evicting if necessary. */ need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; - ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); + ret = i915_gem_execbuffer_reserve(ring, &eb->objects, &need_relocs); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ if (need_relocs) - ret = i915_gem_execbuffer_relocate(dev, eb); + ret = i915_gem_execbuffer_relocate(eb); if (ret) { if (ret == -EFAULT) { ret = i915_gem_execbuffer_relocate_slow(dev, args, file, ring, @@ -1110,7 +1116,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, return -ENOMEM; } ret = copy_from_user(exec_list, - (void __user *)(uintptr_t)args->buffers_ptr, + to_user_ptr(args->buffers_ptr), sizeof(*exec_list) * args->buffer_count); if (ret != 0) { DRM_DEBUG("copy %d exec entries failed %d\n", @@ -1149,7 +1155,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, for (i = 0; i < args->buffer_count; i++) exec_list[i].offset = exec2_list[i].offset; /* ... and back out to userspace */ - ret = copy_to_user((void __user *)(uintptr_t)args->buffers_ptr, + ret = copy_to_user(to_user_ptr(args->buffers_ptr), exec_list, sizeof(*exec_list) * args->buffer_count); if (ret) { @@ -1190,8 +1196,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, return -ENOMEM; } ret = copy_from_user(exec2_list, - (struct drm_i915_relocation_entry __user *) - (uintptr_t) args->buffers_ptr, + to_user_ptr(args->buffers_ptr), sizeof(*exec2_list) * args->buffer_count); if (ret != 0) { DRM_DEBUG("copy %d exec entries failed %d\n", @@ -1203,7 +1208,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list); if (!ret) { /* Copy the new buffer offsets back to the user's exec list. */ - ret = copy_to_user((void __user *)(uintptr_t)args->buffers_ptr, + ret = copy_to_user(to_user_ptr(args->buffers_ptr), exec2_list, sizeof(*exec2_list) * args->buffer_count); if (ret) { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 926a1e2..24a23b3 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -83,7 +83,7 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, { gtt_pte_t *pt_vaddr; gtt_pte_t scratch_pte; - unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES; + unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES; unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; unsigned last_pte, i; @@ -96,7 +96,7 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, if (last_pte > I915_PPGTT_PT_ENTRIES) last_pte = I915_PPGTT_PT_ENTRIES; - pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]); + pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pt]); for (i = first_pte; i < last_pte; i++) pt_vaddr[i] = scratch_pte; @@ -105,7 +105,7 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, num_entries -= last_pte - first_pte; first_pte = 0; - act_pd++; + act_pt++; } } @@ -115,42 +115,26 @@ static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt, enum i915_cache_level cache_level) { gtt_pte_t *pt_vaddr; - unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES; - unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; - unsigned i, j, m, segment_len; - dma_addr_t page_addr; - struct scatterlist *sg; - - /* init sg walking */ - sg = pages->sgl; - i = 0; - segment_len = sg_dma_len(sg) >> PAGE_SHIFT; - m = 0; - - while (i < pages->nents) { - pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]); - - for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) { - page_addr = sg_dma_address(sg) + (m << PAGE_SHIFT); - pt_vaddr[j] = gen6_pte_encode(ppgtt->dev, page_addr, - cache_level); - - /* grab the next page */ - if (++m == segment_len) { - if (++i == pages->nents) - break; - - sg = sg_next(sg); - segment_len = sg_dma_len(sg) >> PAGE_SHIFT; - m = 0; - } - } - - kunmap_atomic(pt_vaddr); + unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES; + unsigned act_pte = first_entry % I915_PPGTT_PT_ENTRIES; + struct sg_page_iter sg_iter; + + pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pt]); + for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) { + dma_addr_t page_addr; + + page_addr = sg_page_iter_dma_address(&sg_iter); + pt_vaddr[act_pte] = gen6_pte_encode(ppgtt->dev, page_addr, + cache_level); + if (++act_pte == I915_PPGTT_PT_ENTRIES) { + kunmap_atomic(pt_vaddr); + act_pt++; + pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pt]); + act_pte = 0; - first_pte = 0; - act_pd++; + } } + kunmap_atomic(pt_vaddr); } static void gen6_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt) @@ -432,21 +416,16 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev, enum i915_cache_level level) { struct drm_i915_private *dev_priv = dev->dev_private; - struct scatterlist *sg = st->sgl; gtt_pte_t __iomem *gtt_entries = (gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry; - int unused, i = 0; - unsigned int len, m = 0; + int i = 0; + struct sg_page_iter sg_iter; dma_addr_t addr; - for_each_sg(st->sgl, sg, st->nents, unused) { - len = sg_dma_len(sg) >> PAGE_SHIFT; - for (m = 0; m < len; m++) { - addr = sg_dma_address(sg) + (m << PAGE_SHIFT); - iowrite32(gen6_pte_encode(dev, addr, level), - >t_entries[i]); - i++; - } + for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) { + addr = sg_page_iter_dma_address(&sg_iter); + iowrite32(gen6_pte_encode(dev, addr, level), >t_entries[i]); + i++; } /* XXX: This serves as a posting read to make sure that the PTE has @@ -752,7 +731,7 @@ static int gen6_gmch_probe(struct drm_device *dev, pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl); - if (IS_GEN7(dev)) + if (IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) *stolen = gen7_get_stolen_size(snb_gmch_ctl); else *stolen = gen6_get_stolen_size(snb_gmch_ctl); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 69d97cb..130d1db 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -312,6 +312,71 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size) return NULL; } +struct drm_i915_gem_object * +i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, + u32 stolen_offset, + u32 gtt_offset, + u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + struct drm_mm_node *stolen; + + if (dev_priv->mm.stolen_base == 0) + return NULL; + + DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n", + stolen_offset, gtt_offset, size); + + /* KISS and expect everything to be page-aligned */ + BUG_ON(stolen_offset & 4095); + BUG_ON(gtt_offset & 4095); + BUG_ON(size & 4095); + + if (WARN_ON(size == 0)) + return NULL; + + stolen = drm_mm_create_block(&dev_priv->mm.stolen, + stolen_offset, size, + false); + if (stolen == NULL) { + DRM_DEBUG_KMS("failed to allocate stolen space\n"); + return NULL; + } + + obj = _i915_gem_object_create_stolen(dev, stolen); + if (obj == NULL) { + DRM_DEBUG_KMS("failed to allocate stolen object\n"); + drm_mm_put_block(stolen); + return NULL; + } + + /* To simplify the initialisation sequence between KMS and GTT, + * we allow construction of the stolen object prior to + * setting up the GTT space. The actual reservation will occur + * later. + */ + if (drm_mm_initialized(&dev_priv->mm.gtt_space)) { + obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space, + gtt_offset, size, + false); + if (obj->gtt_space == NULL) { + DRM_DEBUG_KMS("failed to allocate stolen GTT space\n"); + drm_gem_object_unreference(&obj->base); + return NULL; + } + } else + obj->gtt_space = I915_GTT_RESERVED; + + obj->gtt_offset = gtt_offset; + obj->has_global_gtt_mapping = 1; + + list_add_tail(&obj->gtt_list, &dev_priv->mm.bound_list); + list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list); + + return obj; +} + void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) { diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index abcba2f..c807eb9 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -473,28 +473,29 @@ i915_gem_swizzle_page(struct page *page) void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj) { - struct scatterlist *sg; - int page_count = obj->base.size >> PAGE_SHIFT; + struct sg_page_iter sg_iter; int i; if (obj->bit_17 == NULL) return; - for_each_sg(obj->pages->sgl, sg, page_count, i) { - struct page *page = sg_page(sg); + i = 0; + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) { + struct page *page = sg_page_iter_page(&sg_iter); char new_bit_17 = page_to_phys(page) >> 17; if ((new_bit_17 & 0x1) != (test_bit(i, obj->bit_17) != 0)) { i915_gem_swizzle_page(page); set_page_dirty(page); } + i++; } } void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj) { - struct scatterlist *sg; + struct sg_page_iter sg_iter; int page_count = obj->base.size >> PAGE_SHIFT; int i; @@ -508,11 +509,12 @@ i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj) } } - for_each_sg(obj->pages->sgl, sg, page_count, i) { - struct page *page = sg_page(sg); - if (page_to_phys(page) & (1 << 17)) + i = 0; + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) { + if (page_to_phys(sg_page_iter_page(&sg_iter)) & (1 << 17)) __set_bit(i, obj->bit_17); else __clear_bit(i, obj->bit_17); + i++; } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2cd97d1..4c5bdd0 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -36,6 +36,60 @@ #include "i915_trace.h" #include "intel_drv.h" +static const u32 hpd_ibx[] = { + [HPD_CRT] = SDE_CRT_HOTPLUG, + [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG, + [HPD_PORT_B] = SDE_PORTB_HOTPLUG, + [HPD_PORT_C] = SDE_PORTC_HOTPLUG, + [HPD_PORT_D] = SDE_PORTD_HOTPLUG +}; + +static const u32 hpd_cpt[] = { + [HPD_CRT] = SDE_CRT_HOTPLUG_CPT, + [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG_CPT, + [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT, + [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT, + [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT +}; + +static const u32 hpd_mask_i915[] = { + [HPD_CRT] = CRT_HOTPLUG_INT_EN, + [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_EN, + [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_EN, + [HPD_PORT_B] = PORTB_HOTPLUG_INT_EN, + [HPD_PORT_C] = PORTC_HOTPLUG_INT_EN, + [HPD_PORT_D] = PORTD_HOTPLUG_INT_EN +}; + +static const u32 hpd_status_gen4[] = { + [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, + [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_G4X, + [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_G4X, + [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, + [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, + [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS +}; + +static const u32 hpd_status_i965[] = { + [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, + [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I965, + [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I965, + [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, + [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, + [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS +}; + +static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */ + [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, + [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915, + [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I915, + [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, + [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, + [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS +}; + + + /* For display hotplug interrupt */ static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -47,7 +101,7 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) } } -static inline void +static void ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { if ((dev_priv->irq_mask & mask) != mask) { @@ -60,26 +114,30 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) { - if ((dev_priv->pipestat[pipe] & mask) != mask) { - u32 reg = PIPESTAT(pipe); + u32 reg = PIPESTAT(pipe); + u32 pipestat = I915_READ(reg) & 0x7fff0000; - dev_priv->pipestat[pipe] |= mask; - /* Enable the interrupt, clear any pending status */ - I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); - POSTING_READ(reg); - } + if ((pipestat & mask) == mask) + return; + + /* Enable the interrupt, clear any pending status */ + pipestat |= mask | (mask >> 16); + I915_WRITE(reg, pipestat); + POSTING_READ(reg); } void i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) { - if ((dev_priv->pipestat[pipe] & mask) != 0) { - u32 reg = PIPESTAT(pipe); + u32 reg = PIPESTAT(pipe); + u32 pipestat = I915_READ(reg) & 0x7fff0000; - dev_priv->pipestat[pipe] &= ~mask; - I915_WRITE(reg, dev_priv->pipestat[pipe]); - POSTING_READ(reg); - } + if ((pipestat & mask) == 0) + return; + + pipestat &= ~mask; + I915_WRITE(reg, pipestat); + POSTING_READ(reg); } /** @@ -250,10 +308,9 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, struct timeval *vblank_time, unsigned flags) { - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; - if (pipe < 0 || pipe >= dev_priv->num_pipe) { + if (pipe < 0 || pipe >= INTEL_INFO(dev)->num_pipes) { DRM_ERROR("Invalid crtc %d\n", pipe); return -EINVAL; } @@ -596,7 +653,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); - if (hotplug_status & dev_priv->hotplug_supported_mask) + if (hotplug_status & HOTPLUG_INT_STATUS_I915) queue_work(dev_priv->wq, &dev_priv->hotplug_work); @@ -701,7 +758,7 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 de_iir, gt_iir, de_ier, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir, sde_ier; irqreturn_t ret = IRQ_NONE; int i; @@ -711,6 +768,15 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); + /* Disable south interrupts. We'll only write to SDEIIR once, so further + * interrupts will will be stored on its back queue, and then we'll be + * able to process them after we restore SDEIER (as soon as we restore + * it, we'll get an interrupt if SDEIIR still has something to process + * due to its back queue). */ + sde_ier = I915_READ(SDEIER); + I915_WRITE(SDEIER, 0); + POSTING_READ(SDEIER); + gt_iir = I915_READ(GTIIR); if (gt_iir) { snb_gt_irq_handler(dev, dev_priv, gt_iir); @@ -759,6 +825,8 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); + I915_WRITE(SDEIER, sde_ier); + POSTING_READ(SDEIER); return ret; } @@ -778,7 +846,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir, sde_ier; atomic_inc(&dev_priv->irq_received); @@ -787,6 +855,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); POSTING_READ(DEIER); + /* Disable south interrupts. We'll only write to SDEIIR once, so further + * interrupts will will be stored on its back queue, and then we'll be + * able to process them after we restore SDEIER (as soon as we restore + * it, we'll get an interrupt if SDEIIR still has something to process + * due to its back queue). */ + sde_ier = I915_READ(SDEIER); + I915_WRITE(SDEIER, 0); + POSTING_READ(SDEIER); + de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); pm_iir = I915_READ(GEN6_PMIIR); @@ -849,6 +926,8 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) done: I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); + I915_WRITE(SDEIER, sde_ier); + POSTING_READ(SDEIER); return ret; } @@ -915,6 +994,8 @@ static void i915_error_work_func(struct work_struct *work) for_each_ring(ring, dev_priv, i) wake_up_all(&ring->irq_queue); + intel_display_handle_reset(dev); + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -950,24 +1031,23 @@ static void i915_get_extra_instdone(struct drm_device *dev, #ifdef CONFIG_DEBUG_FS static struct drm_i915_error_object * -i915_error_object_create(struct drm_i915_private *dev_priv, - struct drm_i915_gem_object *src) +i915_error_object_create_sized(struct drm_i915_private *dev_priv, + struct drm_i915_gem_object *src, + const int num_pages) { struct drm_i915_error_object *dst; - int i, count; + int i; u32 reloc_offset; if (src == NULL || src->pages == NULL) return NULL; - count = src->base.size / PAGE_SIZE; - - dst = kmalloc(sizeof(*dst) + count * sizeof(u32 *), GFP_ATOMIC); + dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC); if (dst == NULL) return NULL; reloc_offset = src->gtt_offset; - for (i = 0; i < count; i++) { + for (i = 0; i < num_pages; i++) { unsigned long flags; void *d; @@ -1017,7 +1097,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, reloc_offset += PAGE_SIZE; } - dst->page_count = count; + dst->page_count = num_pages; dst->gtt_offset = src->gtt_offset; return dst; @@ -1028,6 +1108,9 @@ unwind: kfree(dst); return NULL; } +#define i915_error_object_create(dev_priv, src) \ + i915_error_object_create_sized((dev_priv), (src), \ + (src)->base.size>>PAGE_SHIFT) static void i915_error_object_free(struct drm_i915_error_object *obj) @@ -1234,6 +1317,26 @@ static void i915_record_ring_state(struct drm_device *dev, error->cpu_ring_tail[ring->id] = ring->tail; } + +static void i915_gem_record_active_context(struct intel_ring_buffer *ring, + struct drm_i915_error_state *error, + struct drm_i915_error_ring *ering) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct drm_i915_gem_object *obj; + + /* Currently render ring is the only HW context user */ + if (ring->id != RCS || !error->ccid) + return; + + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { + if ((error->ccid & PAGE_MASK) == obj->gtt_offset) { + ering->ctx = i915_error_object_create_sized(dev_priv, + obj, 1); + } + } +} + static void i915_gem_record_rings(struct drm_device *dev, struct drm_i915_error_state *error) { @@ -1251,6 +1354,9 @@ static void i915_gem_record_rings(struct drm_device *dev, error->ring[i].ringbuffer = i915_error_object_create(dev_priv, ring->obj); + + i915_gem_record_active_context(ring, error, &error->ring[i]); + count = 0; list_for_each_entry(request, &ring->request_list, list) count++; @@ -1306,14 +1412,15 @@ static void i915_capture_error_state(struct drm_device *dev) return; } - DRM_INFO("capturing error event; look for more information in" + DRM_INFO("capturing error event; look for more information in " "/sys/kernel/debug/dri/%d/i915_error_state\n", dev->primary->index); kref_init(&error->ref); error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); - error->ccid = I915_READ(CCID); + if (HAS_HW_CONTEXTS(dev)) + error->ccid = I915_READ(CCID); if (HAS_PCH_SPLIT(dev)) error->ier = I915_READ(DEIER) | I915_READ(GTIER); @@ -1334,8 +1441,9 @@ static void i915_capture_error_state(struct drm_device *dev) else if (INTEL_INFO(dev)->gen == 6) error->forcewake = I915_READ(FORCEWAKE); - for_each_pipe(pipe) - error->pipestat[pipe] = I915_READ(PIPESTAT(pipe)); + if (!HAS_PCH_SPLIT(dev)) + for_each_pipe(pipe) + error->pipestat[pipe] = I915_READ(PIPESTAT(pipe)); if (INTEL_INFO(dev)->gen >= 6) { error->error = I915_READ(ERROR_GEN6); @@ -1545,7 +1653,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) queue_work(dev_priv->wq, &dev_priv->gpu_error.work); } -static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) +static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; @@ -1755,6 +1863,37 @@ static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err) return false; } +static bool semaphore_passed(struct intel_ring_buffer *ring) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + u32 acthd = intel_ring_get_active_head(ring) & HEAD_ADDR; + struct intel_ring_buffer *signaller; + u32 cmd, ipehr, acthd_min; + + ipehr = I915_READ(RING_IPEHR(ring->mmio_base)); + if ((ipehr & ~(0x3 << 16)) != + (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER)) + return false; + + /* ACTHD is likely pointing to the dword after the actual command, + * so scan backwards until we find the MBOX. + */ + acthd_min = max((int)acthd - 3 * 4, 0); + do { + cmd = ioread32(ring->virtual_start + acthd); + if (cmd == ipehr) + break; + + acthd -= 4; + if (acthd < acthd_min) + return false; + } while (1); + + signaller = &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3]; + return i915_seqno_passed(signaller->get_seqno(signaller, false), + ioread32(ring->virtual_start+acthd+4)+1); +} + static bool kick_ring(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; @@ -1766,6 +1905,15 @@ static bool kick_ring(struct intel_ring_buffer *ring) I915_WRITE_CTL(ring, tmp); return true; } + + if (INTEL_INFO(dev)->gen >= 6 && + tmp & RING_WAIT_SEMAPHORE && + semaphore_passed(ring)) { + DRM_ERROR("Kicking stuck semaphore on %s\n", + ring->name); + I915_WRITE_CTL(ring, tmp); + return true; + } return false; } @@ -1881,7 +2029,13 @@ static void ironlake_irq_preinstall(struct drm_device *dev) /* south display irq */ I915_WRITE(SDEIMR, 0xffffffff); - I915_WRITE(SDEIER, 0x0); + /* + * SDEIER is also touched by the interrupt handler to work around missed + * PCH interrupts. Hence we can't update it after the interrupt handler + * is enabled - instead we unconditionally enable all PCH interrupt + * sources here, but then only unmask them as needed with SDEIMR. + */ + I915_WRITE(SDEIER, 0xffffffff); POSTING_READ(SDEIER); } @@ -1917,18 +2071,30 @@ static void valleyview_irq_preinstall(struct drm_device *dev) POSTING_READ(VLV_IER); } -/* - * Enable digital hotplug on the PCH, and configure the DP short pulse - * duration to 2ms (which is the minimum in the Display Port spec) - * - * This register is the same on all known PCH chips. - */ - -static void ibx_enable_hotplug(struct drm_device *dev) +static void ibx_hpd_irq_setup(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *intel_encoder; + u32 mask = ~I915_READ(SDEIMR); + u32 hotplug; + if (HAS_PCH_IBX(dev)) { + list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) + mask |= hpd_ibx[intel_encoder->hpd_pin]; + } else { + list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) + mask |= hpd_cpt[intel_encoder->hpd_pin]; + } + + I915_WRITE(SDEIMR, ~mask); + + /* + * Enable digital hotplug on the PCH, and configure the DP short pulse + * duration to 2ms (which is the minimum in the Display Port spec) + * + * This register is the same on all known PCH chips. + */ hotplug = I915_READ(PCH_PORT_HOTPLUG); hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK); hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; @@ -1943,20 +2109,11 @@ static void ibx_irq_postinstall(struct drm_device *dev) u32 mask; if (HAS_PCH_IBX(dev)) - mask = SDE_HOTPLUG_MASK | - SDE_GMBUS | - SDE_AUX_MASK; + mask = SDE_GMBUS | SDE_AUX_MASK; else - mask = SDE_HOTPLUG_MASK_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT; - + mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); I915_WRITE(SDEIMR, ~mask); - I915_WRITE(SDEIER, mask); - POSTING_READ(SDEIER); - - ibx_enable_hotplug(dev); } static int ironlake_irq_postinstall(struct drm_device *dev) @@ -2067,9 +2224,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - dev_priv->pipestat[0] = 0; - dev_priv->pipestat[1] = 0; - /* Hack for broken MSIs on VLV */ pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000); pci_read_config_word(dev->pdev, 0x98, &msid); @@ -2113,30 +2267,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) return 0; } -static void valleyview_hpd_irq_setup(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); - - /* Note HDMI and DP share bits */ - if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) - hotplug_en |= PORTB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) - hotplug_en |= PORTC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) - hotplug_en |= PORTD_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) - hotplug_en |= SDVOC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) - hotplug_en |= SDVOB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { - hotplug_en |= CRT_HOTPLUG_INT_EN; - hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; - } - - I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); -} - static void valleyview_irq_uninstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -2199,9 +2329,6 @@ static int i8xx_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->pipestat[0] = 0; - dev_priv->pipestat[1] = 0; - I915_WRITE16(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); @@ -2224,6 +2351,37 @@ static int i8xx_irq_postinstall(struct drm_device *dev) return 0; } +/* + * Returns true when a page flip has completed. + */ +static bool i8xx_handle_vblank(struct drm_device *dev, + int pipe, u16 iir) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(pipe); + + if (!drm_handle_vblank(dev, pipe)) + return false; + + if ((iir & flip_pending) == 0) + return false; + + intel_prepare_page_flip(dev, pipe); + + /* We detect FlipDone by looking for the change in PendingFlip from '1' + * to '0' on the following vblank, i.e. IIR has the Pendingflip + * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence + * the flip is completed (no longer pending). Since this doesn't raise + * an interrupt per se, we watch for the change at vblank. + */ + if (I915_READ16(ISR) & flip_pending) + return false; + + intel_finish_page_flip(dev, pipe); + + return true; +} + static irqreturn_t i8xx_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -2279,22 +2437,12 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) notify_ring(dev, &dev_priv->ring[RCS]); if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS && - drm_handle_vblank(dev, 0)) { - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { - intel_prepare_page_flip(dev, 0); - intel_finish_page_flip(dev, 0); - flip_mask &= ~I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT; - } - } + i8xx_handle_vblank(dev, 0, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(0); if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS && - drm_handle_vblank(dev, 1)) { - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { - intel_prepare_page_flip(dev, 1); - intel_finish_page_flip(dev, 1); - flip_mask &= ~I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; - } - } + i8xx_handle_vblank(dev, 1, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(1); iir = new_iir; } @@ -2342,9 +2490,6 @@ static int i915_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - dev_priv->pipestat[0] = 0; - dev_priv->pipestat[1] = 0; - I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); /* Unmask the interrupts that we always want on. */ @@ -2382,33 +2527,35 @@ static int i915_irq_postinstall(struct drm_device *dev) return 0; } -static void i915_hpd_irq_setup(struct drm_device *dev) +/* + * Returns true when a page flip has completed. + */ +static bool i915_handle_vblank(struct drm_device *dev, + int plane, int pipe, u32 iir) { - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug_en; + drm_i915_private_t *dev_priv = dev->dev_private; + u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane); - if (I915_HAS_HOTPLUG(dev)) { - hotplug_en = I915_READ(PORT_HOTPLUG_EN); + if (!drm_handle_vblank(dev, pipe)) + return false; - if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) - hotplug_en |= PORTB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) - hotplug_en |= PORTC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) - hotplug_en |= PORTD_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) - hotplug_en |= SDVOC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) - hotplug_en |= SDVOB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { - hotplug_en |= CRT_HOTPLUG_INT_EN; - hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; - } + if ((iir & flip_pending) == 0) + return false; - /* Ignore TV since it's buggy */ + intel_prepare_page_flip(dev, plane); - I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - } + /* We detect FlipDone by looking for the change in PendingFlip from '1' + * to '0' on the following vblank, i.e. IIR has the Pendingflip + * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence + * the flip is completed (no longer pending). Since this doesn't raise + * an interrupt per se, we watch for the change at vblank. + */ + if (I915_READ(ISR) & flip_pending) + return false; + + intel_finish_page_flip(dev, pipe); + + return true; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -2420,10 +2567,6 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) u32 flip_mask = I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; - u32 flip[2] = { - I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT, - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT - }; int pipe, ret = IRQ_NONE; atomic_inc(&dev_priv->irq_received); @@ -2467,7 +2610,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); - if (hotplug_status & dev_priv->hotplug_supported_mask) + if (hotplug_status & HOTPLUG_INT_STATUS_I915) queue_work(dev_priv->wq, &dev_priv->hotplug_work); @@ -2485,14 +2628,10 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) int plane = pipe; if (IS_MOBILE(dev)) plane = !plane; + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && - drm_handle_vblank(dev, pipe)) { - if (iir & flip[plane]) { - intel_prepare_page_flip(dev, plane); - intel_finish_page_flip(dev, pipe); - flip_mask &= ~flip[plane]; - } - } + i915_handle_vblank(dev, plane, pipe, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(plane); if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) blc_event = true; @@ -2581,13 +2720,13 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); enable_mask = ~dev_priv->irq_mask; + enable_mask &= ~(I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT); enable_mask |= I915_USER_INTERRUPT; if (IS_G4X(dev)) enable_mask |= I915_BSD_USER_INTERRUPT; - dev_priv->pipestat[0] = 0; - dev_priv->pipestat[1] = 0; i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); /* @@ -2617,45 +2756,32 @@ static int i965_irq_postinstall(struct drm_device *dev) return 0; } -static void i965_hpd_irq_setup(struct drm_device *dev) +static void i915_hpd_irq_setup(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *encoder; u32 hotplug_en; - /* Note HDMI and DP share hotplug bits */ - hotplug_en = 0; - if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) - hotplug_en |= PORTB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) - hotplug_en |= PORTC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) - hotplug_en |= PORTD_HOTPLUG_INT_EN; - if (IS_G4X(dev)) { - if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X) - hotplug_en |= SDVOC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_G4X) - hotplug_en |= SDVOB_HOTPLUG_INT_EN; - } else { - if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I965) - hotplug_en |= SDVOC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I965) - hotplug_en |= SDVOB_HOTPLUG_INT_EN; - } - if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { - hotplug_en |= CRT_HOTPLUG_INT_EN; - + if (I915_HAS_HOTPLUG(dev)) { + hotplug_en = I915_READ(PORT_HOTPLUG_EN); + hotplug_en &= ~HOTPLUG_INT_EN_MASK; + /* Note HDMI and DP share hotplug bits */ + /* enable bits are the same for all generations */ + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) + hotplug_en |= hpd_mask_i915[encoder->hpd_pin]; /* Programming the CRT detection parameters tends to generate a spurious hotplug event about three seconds later. So just do it once. - */ + */ if (IS_G4X(dev)) hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; + hotplug_en &= ~CRT_HOTPLUG_VOLTAGE_COMPARE_MASK; hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; - } - - /* Ignore TV since it's buggy */ - I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); + /* Ignore TV since it's buggy */ + I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); + } } static irqreturn_t i965_irq_handler(int irq, void *arg) @@ -2667,6 +2793,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) unsigned long irqflags; int irq_received; int ret = IRQ_NONE, pipe; + u32 flip_mask = + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; atomic_inc(&dev_priv->irq_received); @@ -2675,7 +2804,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) for (;;) { bool blc_event = false; - irq_received = iir != 0; + irq_received = (iir & ~flip_mask) != 0; /* Can't rely on pipestat interrupt bit in iir as it might * have been cleared after the pipestat interrupt was received. @@ -2714,7 +2843,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); - if (hotplug_status & dev_priv->hotplug_supported_mask) + if (hotplug_status & (IS_G4X(dev) ? + HOTPLUG_INT_STATUS_G4X : + HOTPLUG_INT_STATUS_I965)) queue_work(dev_priv->wq, &dev_priv->hotplug_work); @@ -2722,7 +2853,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } - I915_WRITE(IIR, iir); + I915_WRITE(IIR, iir & ~flip_mask); new_iir = I915_READ(IIR); /* Flush posted writes */ if (iir & I915_USER_INTERRUPT) @@ -2730,18 +2861,10 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (iir & I915_BSD_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VCS]); - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) - intel_prepare_page_flip(dev, 0); - - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) - intel_prepare_page_flip(dev, 1); - for_each_pipe(pipe) { if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && - drm_handle_vblank(dev, pipe)) { - i915_pageflip_stall_check(dev, pipe); - intel_finish_page_flip(dev, pipe); - } + i915_handle_vblank(dev, pipe, pipe, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe); if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) blc_event = true; @@ -2835,7 +2958,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; - dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup; + dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; @@ -2844,6 +2967,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; + dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2851,6 +2975,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; + dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; } else { if (INTEL_INFO(dev)->gen == 2) { dev->driver->irq_preinstall = i8xx_irq_preinstall; @@ -2868,7 +2993,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_postinstall = i965_irq_postinstall; dev->driver->irq_uninstall = i965_irq_uninstall; dev->driver->irq_handler = i965_irq_handler; - dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup; + dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 527b664..058686c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -91,6 +91,7 @@ #define GRDOM_FULL (0<<2) #define GRDOM_RENDER (1<<2) #define GRDOM_MEDIA (3<<2) +#define GRDOM_MASK (3<<2) #define GRDOM_RESET_ENABLE (1<<0) #define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */ @@ -121,6 +122,7 @@ #define GAM_ECOCHK 0x4090 #define ECOCHK_SNB_BIT (1<<10) +#define HSW_ECOCHK_ARB_PRIO_SOL (1<<6) #define ECOCHK_PPGTT_CACHE64B (0x3<<3) #define ECOCHK_PPGTT_CACHE4B (0x0<<3) @@ -522,6 +524,9 @@ #define GEN7_ERR_INT 0x44040 #define ERR_INT_MMIO_UNCLAIMED (1<<13) +#define FPGA_DBG 0x42300 +#define FPGA_DBG_RM_NOCLAIM (1<<31) + #define DERRMR 0x44050 /* GM45+ chicken bits -- debug workaround bits that may be required @@ -591,6 +596,7 @@ #define I915_USER_INTERRUPT (1<<1) #define I915_ASLE_INTERRUPT (1<<0) #define I915_BSD_USER_INTERRUPT (1<<25) +#define DISPLAY_PLANE_FLIP_PENDING(plane) (1<<(11-(plane))) /* A and B only */ #define EIR 0x020b0 #define EMR 0x020b4 #define ESR 0x020b8 @@ -1613,9 +1619,9 @@ #define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16) #define ADPA_USE_VGA_HVPOLARITY (1<<15) #define ADPA_SETS_HVPOLARITY 0 -#define ADPA_VSYNC_CNTL_DISABLE (1<<11) +#define ADPA_VSYNC_CNTL_DISABLE (1<<10) #define ADPA_VSYNC_CNTL_ENABLE 0 -#define ADPA_HSYNC_CNTL_DISABLE (1<<10) +#define ADPA_HSYNC_CNTL_DISABLE (1<<11) #define ADPA_HSYNC_CNTL_ENABLE 0 #define ADPA_VSYNC_ACTIVE_HIGH (1<<4) #define ADPA_VSYNC_ACTIVE_LOW 0 @@ -1637,6 +1643,12 @@ #define SDVOC_HOTPLUG_INT_EN (1 << 25) #define TV_HOTPLUG_INT_EN (1 << 18) #define CRT_HOTPLUG_INT_EN (1 << 9) +#define HOTPLUG_INT_EN_MASK (PORTB_HOTPLUG_INT_EN | \ + PORTC_HOTPLUG_INT_EN | \ + PORTD_HOTPLUG_INT_EN | \ + SDVOC_HOTPLUG_INT_EN | \ + SDVOB_HOTPLUG_INT_EN | \ + CRT_HOTPLUG_INT_EN) #define CRT_HOTPLUG_FORCE_DETECT (1 << 3) #define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8) /* must use period 64 on GM45 according to docs */ @@ -1675,43 +1687,84 @@ #define SDVOB_HOTPLUG_INT_STATUS_I965 (3 << 2) #define SDVOC_HOTPLUG_INT_STATUS_I915 (1 << 7) #define SDVOB_HOTPLUG_INT_STATUS_I915 (1 << 6) - -/* SDVO port control */ -#define SDVOB 0x61140 -#define SDVOC 0x61160 -#define SDVO_ENABLE (1 << 31) -#define SDVO_PIPE_B_SELECT (1 << 30) -#define SDVO_STALL_SELECT (1 << 29) -#define SDVO_INTERRUPT_ENABLE (1 << 26) +#define HOTPLUG_INT_STATUS_G4X (CRT_HOTPLUG_INT_STATUS | \ + SDVOB_HOTPLUG_INT_STATUS_G4X | \ + SDVOC_HOTPLUG_INT_STATUS_G4X | \ + PORTB_HOTPLUG_INT_STATUS | \ + PORTC_HOTPLUG_INT_STATUS | \ + PORTD_HOTPLUG_INT_STATUS) + +#define HOTPLUG_INT_STATUS_I965 (CRT_HOTPLUG_INT_STATUS | \ + SDVOB_HOTPLUG_INT_STATUS_I965 | \ + SDVOC_HOTPLUG_INT_STATUS_I965 | \ + PORTB_HOTPLUG_INT_STATUS | \ + PORTC_HOTPLUG_INT_STATUS | \ + PORTD_HOTPLUG_INT_STATUS) + +#define HOTPLUG_INT_STATUS_I915 (CRT_HOTPLUG_INT_STATUS | \ + SDVOB_HOTPLUG_INT_STATUS_I915 | \ + SDVOC_HOTPLUG_INT_STATUS_I915 | \ + PORTB_HOTPLUG_INT_STATUS | \ + PORTC_HOTPLUG_INT_STATUS | \ + PORTD_HOTPLUG_INT_STATUS) + +/* SDVO and HDMI port control. + * The same register may be used for SDVO or HDMI */ +#define GEN3_SDVOB 0x61140 +#define GEN3_SDVOC 0x61160 +#define GEN4_HDMIB GEN3_SDVOB +#define GEN4_HDMIC GEN3_SDVOC +#define PCH_SDVOB 0xe1140 +#define PCH_HDMIB PCH_SDVOB +#define PCH_HDMIC 0xe1150 +#define PCH_HDMID 0xe1160 + +/* Gen 3 SDVO bits: */ +#define SDVO_ENABLE (1 << 31) +#define SDVO_PIPE_SEL(pipe) ((pipe) << 30) +#define SDVO_PIPE_SEL_MASK (1 << 30) +#define SDVO_PIPE_B_SELECT (1 << 30) +#define SDVO_STALL_SELECT (1 << 29) +#define SDVO_INTERRUPT_ENABLE (1 << 26) /** * 915G/GM SDVO pixel multiplier. - * * Programmed value is multiplier - 1, up to 5x. - * * \sa DPLL_MD_UDI_MULTIPLIER_MASK */ -#define SDVO_PORT_MULTIPLY_MASK (7 << 23) +#define SDVO_PORT_MULTIPLY_MASK (7 << 23) #define SDVO_PORT_MULTIPLY_SHIFT 23 -#define SDVO_PHASE_SELECT_MASK (15 << 19) -#define SDVO_PHASE_SELECT_DEFAULT (6 << 19) -#define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) -#define SDVOC_GANG_MODE (1 << 16) -#define SDVO_ENCODING_SDVO (0x0 << 10) -#define SDVO_ENCODING_HDMI (0x2 << 10) -/** Requird for HDMI operation */ -#define SDVO_NULL_PACKETS_DURING_VSYNC (1 << 9) -#define SDVO_COLOR_RANGE_16_235 (1 << 8) -#define SDVO_BORDER_ENABLE (1 << 7) -#define SDVO_AUDIO_ENABLE (1 << 6) -/** New with 965, default is to be set */ -#define SDVO_VSYNC_ACTIVE_HIGH (1 << 4) -/** New with 965, default is to be set */ -#define SDVO_HSYNC_ACTIVE_HIGH (1 << 3) -#define SDVOB_PCIE_CONCURRENCY (1 << 3) -#define SDVO_DETECTED (1 << 2) +#define SDVO_PHASE_SELECT_MASK (15 << 19) +#define SDVO_PHASE_SELECT_DEFAULT (6 << 19) +#define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) +#define SDVOC_GANG_MODE (1 << 16) /* Port C only */ +#define SDVO_BORDER_ENABLE (1 << 7) /* SDVO only */ +#define SDVOB_PCIE_CONCURRENCY (1 << 3) /* Port B only */ +#define SDVO_DETECTED (1 << 2) /* Bits to be preserved when writing */ -#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14) | (1 << 26)) -#define SDVOC_PRESERVE_MASK ((1 << 17) | (1 << 26)) +#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14) | \ + SDVO_INTERRUPT_ENABLE) +#define SDVOC_PRESERVE_MASK ((1 << 17) | SDVO_INTERRUPT_ENABLE) + +/* Gen 4 SDVO/HDMI bits: */ +#define SDVO_COLOR_FORMAT_8bpc (0 << 26) +#define SDVO_ENCODING_SDVO (0 << 10) +#define SDVO_ENCODING_HDMI (2 << 10) +#define HDMI_MODE_SELECT_HDMI (1 << 9) /* HDMI only */ +#define HDMI_MODE_SELECT_DVI (0 << 9) /* HDMI only */ +#define HDMI_COLOR_RANGE_16_235 (1 << 8) /* HDMI only */ +#define SDVO_AUDIO_ENABLE (1 << 6) +/* VSYNC/HSYNC bits new with 965, default is to be set */ +#define SDVO_VSYNC_ACTIVE_HIGH (1 << 4) +#define SDVO_HSYNC_ACTIVE_HIGH (1 << 3) + +/* Gen 5 (IBX) SDVO/HDMI bits: */ +#define HDMI_COLOR_FORMAT_12bpc (3 << 26) /* HDMI only */ +#define SDVOB_HOTPLUG_ENABLE (1 << 23) /* SDVO only */ + +/* Gen 6 (CPT) SDVO/HDMI bits: */ +#define SDVO_PIPE_SEL_CPT(pipe) ((pipe) << 29) +#define SDVO_PIPE_SEL_MASK_CPT (3 << 29) + /* DVO port control */ #define DVOA 0x61120 @@ -1898,7 +1951,7 @@ #define PFIT_AUTO_RATIOS (dev_priv->info->display_mmio_offset + 0x61238) /* Backlight control */ -#define BLC_PWM_CTL2 0x61250 /* 965+ only */ +#define BLC_PWM_CTL2 (dev_priv->info->display_mmio_offset + 0x61250) /* 965+ only */ #define BLM_PWM_ENABLE (1 << 31) #define BLM_COMBINATION_MODE (1 << 30) /* gen4 only */ #define BLM_PIPE_SELECT (1 << 29) @@ -1917,7 +1970,7 @@ #define BLM_PHASE_IN_COUNT_MASK (0xff << 8) #define BLM_PHASE_IN_INCR_SHIFT (0) #define BLM_PHASE_IN_INCR_MASK (0xff << 0) -#define BLC_PWM_CTL 0x61254 +#define BLC_PWM_CTL (dev_priv->info->display_mmio_offset + 0x61254) /* * This is the most significant 15 bits of the number of backlight cycles in a * complete cycle of the modulated backlight control. @@ -1939,7 +1992,7 @@ #define BACKLIGHT_DUTY_CYCLE_MASK_PNV (0xfffe) #define BLM_POLARITY_PNV (1 << 0) /* pnv only */ -#define BLC_HIST_CTL 0x61260 +#define BLC_HIST_CTL (dev_priv->info->display_mmio_offset + 0x61260) /* New registers for PCH-split platforms. Safe where new bits show up, the * register layout machtes with gen4 BLC_PWM_CTL[12]. */ @@ -2776,6 +2829,8 @@ #define DSPFW_HPLL_CURSOR_SHIFT 16 #define DSPFW_HPLL_CURSOR_MASK (0x3f<<16) #define DSPFW_HPLL_SR_MASK (0x1ff) +#define DSPFW4 (dev_priv->info->display_mmio_offset + 0x70070) +#define DSPFW7 (dev_priv->info->display_mmio_offset + 0x7007c) /* drain latency register values*/ #define DRAIN_LATENCY_PRECISION_32 32 @@ -3233,6 +3288,63 @@ #define SPRGAMC(pipe) _PIPE(pipe, _SPRA_GAMC, _SPRB_GAMC) #define SPRSURFLIVE(pipe) _PIPE(pipe, _SPRA_SURFLIVE, _SPRB_SURFLIVE) +#define _SPACNTR 0x72180 +#define SP_ENABLE (1<<31) +#define SP_GEAMMA_ENABLE (1<<30) +#define SP_PIXFORMAT_MASK (0xf<<26) +#define SP_FORMAT_YUV422 (0<<26) +#define SP_FORMAT_BGR565 (5<<26) +#define SP_FORMAT_BGRX8888 (6<<26) +#define SP_FORMAT_BGRA8888 (7<<26) +#define SP_FORMAT_RGBX1010102 (8<<26) +#define SP_FORMAT_RGBA1010102 (9<<26) +#define SP_FORMAT_RGBX8888 (0xe<<26) +#define SP_FORMAT_RGBA8888 (0xf<<26) +#define SP_SOURCE_KEY (1<<22) +#define SP_YUV_BYTE_ORDER_MASK (3<<16) +#define SP_YUV_ORDER_YUYV (0<<16) +#define SP_YUV_ORDER_UYVY (1<<16) +#define SP_YUV_ORDER_YVYU (2<<16) +#define SP_YUV_ORDER_VYUY (3<<16) +#define SP_TILED (1<<10) +#define _SPALINOFF 0x72184 +#define _SPASTRIDE 0x72188 +#define _SPAPOS 0x7218c +#define _SPASIZE 0x72190 +#define _SPAKEYMINVAL 0x72194 +#define _SPAKEYMSK 0x72198 +#define _SPASURF 0x7219c +#define _SPAKEYMAXVAL 0x721a0 +#define _SPATILEOFF 0x721a4 +#define _SPACONSTALPHA 0x721a8 +#define _SPAGAMC 0x721f4 + +#define _SPBCNTR 0x72280 +#define _SPBLINOFF 0x72284 +#define _SPBSTRIDE 0x72288 +#define _SPBPOS 0x7228c +#define _SPBSIZE 0x72290 +#define _SPBKEYMINVAL 0x72294 +#define _SPBKEYMSK 0x72298 +#define _SPBSURF 0x7229c +#define _SPBKEYMAXVAL 0x722a0 +#define _SPBTILEOFF 0x722a4 +#define _SPBCONSTALPHA 0x722a8 +#define _SPBGAMC 0x722f4 + +#define SPCNTR(pipe, plane) _PIPE(pipe * 2 + plane, _SPACNTR, _SPBCNTR) +#define SPLINOFF(pipe, plane) _PIPE(pipe * 2 + plane, _SPALINOFF, _SPBLINOFF) +#define SPSTRIDE(pipe, plane) _PIPE(pipe * 2 + plane, _SPASTRIDE, _SPBSTRIDE) +#define SPPOS(pipe, plane) _PIPE(pipe * 2 + plane, _SPAPOS, _SPBPOS) +#define SPSIZE(pipe, plane) _PIPE(pipe * 2 + plane, _SPASIZE, _SPBSIZE) +#define SPKEYMINVAL(pipe, plane) _PIPE(pipe * 2 + plane, _SPAKEYMINVAL, _SPBKEYMINVAL) +#define SPKEYMSK(pipe, plane) _PIPE(pipe * 2 + plane, _SPAKEYMSK, _SPBKEYMSK) +#define SPSURF(pipe, plane) _PIPE(pipe * 2 + plane, _SPASURF, _SPBSURF) +#define SPKEYMAXVAL(pipe, plane) _PIPE(pipe * 2 + plane, _SPAKEYMAXVAL, _SPBKEYMAXVAL) +#define SPTILEOFF(pipe, plane) _PIPE(pipe * 2 + plane, _SPATILEOFF, _SPBTILEOFF) +#define SPCONSTALPHA(pipe, plane) _PIPE(pipe * 2 + plane, _SPACONSTALPHA, _SPBCONSTALPHA) +#define SPGAMC(pipe, plane) _PIPE(pipe * 2 + plane, _SPAGAMC, _SPBGAMC) + /* VBIOS regs */ #define VGACNTRL 0x71400 # define VGA_DISP_DISABLE (1 << 31) @@ -3508,7 +3620,11 @@ #define SDE_PORTC_HOTPLUG (1 << 9) #define SDE_PORTB_HOTPLUG (1 << 8) #define SDE_SDVOB_HOTPLUG (1 << 6) -#define SDE_HOTPLUG_MASK (0xf << 8) +#define SDE_HOTPLUG_MASK (SDE_CRT_HOTPLUG | \ + SDE_SDVOB_HOTPLUG | \ + SDE_PORTB_HOTPLUG | \ + SDE_PORTC_HOTPLUG | \ + SDE_PORTD_HOTPLUG) #define SDE_TRANSB_CRC_DONE (1 << 5) #define SDE_TRANSB_CRC_ERR (1 << 4) #define SDE_TRANSB_FIFO_UNDER (1 << 3) @@ -3531,7 +3647,9 @@ #define SDE_PORTC_HOTPLUG_CPT (1 << 22) #define SDE_PORTB_HOTPLUG_CPT (1 << 21) #define SDE_CRT_HOTPLUG_CPT (1 << 19) +#define SDE_SDVOB_HOTPLUG_CPT (1 << 18) #define SDE_HOTPLUG_MASK_CPT (SDE_CRT_HOTPLUG_CPT | \ + SDE_SDVOB_HOTPLUG_CPT | \ SDE_PORTD_HOTPLUG_CPT | \ SDE_PORTC_HOTPLUG_CPT | \ SDE_PORTB_HOTPLUG_CPT) @@ -3754,14 +3872,16 @@ #define HSW_VIDEO_DIP_VSC_ECC_B 0x61344 #define HSW_VIDEO_DIP_GCP_B 0x61210 -#define HSW_TVIDEO_DIP_CTL(pipe) \ - _PIPE(pipe, HSW_VIDEO_DIP_CTL_A, HSW_VIDEO_DIP_CTL_B) -#define HSW_TVIDEO_DIP_AVI_DATA(pipe) \ - _PIPE(pipe, HSW_VIDEO_DIP_AVI_DATA_A, HSW_VIDEO_DIP_AVI_DATA_B) -#define HSW_TVIDEO_DIP_SPD_DATA(pipe) \ - _PIPE(pipe, HSW_VIDEO_DIP_SPD_DATA_A, HSW_VIDEO_DIP_SPD_DATA_B) -#define HSW_TVIDEO_DIP_GCP(pipe) \ - _PIPE(pipe, HSW_VIDEO_DIP_GCP_A, HSW_VIDEO_DIP_GCP_B) +#define HSW_TVIDEO_DIP_CTL(trans) \ + _TRANSCODER(trans, HSW_VIDEO_DIP_CTL_A, HSW_VIDEO_DIP_CTL_B) +#define HSW_TVIDEO_DIP_AVI_DATA(trans) \ + _TRANSCODER(trans, HSW_VIDEO_DIP_AVI_DATA_A, HSW_VIDEO_DIP_AVI_DATA_B) +#define HSW_TVIDEO_DIP_SPD_DATA(trans) \ + _TRANSCODER(trans, HSW_VIDEO_DIP_SPD_DATA_A, HSW_VIDEO_DIP_SPD_DATA_B) +#define HSW_TVIDEO_DIP_GCP(trans) \ + _TRANSCODER(trans, HSW_VIDEO_DIP_GCP_A, HSW_VIDEO_DIP_GCP_B) +#define HSW_TVIDEO_DIP_VSC_DATA(trans) \ + _TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B) #define _TRANS_HTOTAL_B 0xe1000 #define _TRANS_HBLANK_B 0xe1004 @@ -3976,34 +4096,6 @@ #define FDI_PLL_CTL_1 0xfe000 #define FDI_PLL_CTL_2 0xfe004 -/* or SDVOB */ -#define HDMIB 0xe1140 -#define PORT_ENABLE (1 << 31) -#define TRANSCODER(pipe) ((pipe) << 30) -#define TRANSCODER_CPT(pipe) ((pipe) << 29) -#define TRANSCODER_MASK (1 << 30) -#define TRANSCODER_MASK_CPT (3 << 29) -#define COLOR_FORMAT_8bpc (0) -#define COLOR_FORMAT_12bpc (3 << 26) -#define SDVOB_HOTPLUG_ENABLE (1 << 23) -#define SDVO_ENCODING (0) -#define TMDS_ENCODING (2 << 10) -#define NULL_PACKET_VSYNC_ENABLE (1 << 9) -/* CPT */ -#define HDMI_MODE_SELECT (1 << 9) -#define DVI_MODE_SELECT (0) -#define SDVOB_BORDER_ENABLE (1 << 7) -#define AUDIO_ENABLE (1 << 6) -#define VSYNC_ACTIVE_HIGH (1 << 4) -#define HSYNC_ACTIVE_HIGH (1 << 3) -#define PORT_DETECTED (1 << 2) - -/* PCH SDVOB multiplex with HDMIB */ -#define PCH_SDVOB HDMIB - -#define HDMIC 0xe1150 -#define HDMID 0xe1160 - #define PCH_LVDS 0xe1180 #define LVDS_DETECTED (1 << 1) @@ -4020,6 +4112,15 @@ #define PIPEB_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6130c) #define PIPEB_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61310) +#define VLV_PIPE_PP_STATUS(pipe) _PIPE(pipe, PIPEA_PP_STATUS, PIPEB_PP_STATUS) +#define VLV_PIPE_PP_CONTROL(pipe) _PIPE(pipe, PIPEA_PP_CONTROL, PIPEB_PP_CONTROL) +#define VLV_PIPE_PP_ON_DELAYS(pipe) \ + _PIPE(pipe, PIPEA_PP_ON_DELAYS, PIPEB_PP_ON_DELAYS) +#define VLV_PIPE_PP_OFF_DELAYS(pipe) \ + _PIPE(pipe, PIPEA_PP_OFF_DELAYS, PIPEB_PP_OFF_DELAYS) +#define VLV_PIPE_PP_DIVISOR(pipe) \ + _PIPE(pipe, PIPEA_PP_DIVISOR, PIPEB_PP_DIVISOR) + #define PCH_PP_STATUS 0xc7200 #define PCH_PP_CONTROL 0xc7204 #define PANEL_UNLOCK_REGS (0xabcd << 16) @@ -4149,8 +4250,12 @@ #define FORCEWAKE 0xA18C #define FORCEWAKE_VLV 0x1300b0 #define FORCEWAKE_ACK_VLV 0x1300b4 +#define FORCEWAKE_MEDIA_VLV 0x1300b8 +#define FORCEWAKE_ACK_MEDIA_VLV 0x1300bc #define FORCEWAKE_ACK_HSW 0x130044 #define FORCEWAKE_ACK 0x130090 +#define VLV_GTLC_WAKE_CTRL 0x130090 +#define VLV_GTLC_PW_STATUS 0x130094 #define FORCEWAKE_MT 0xa188 /* multi-threaded */ #define FORCEWAKE_KERNEL 0x1 #define FORCEWAKE_USER 0x2 @@ -4184,6 +4289,7 @@ #define GEN6_RPNSWREQ 0xA008 #define GEN6_TURBO_DISABLE (1<<31) #define GEN6_FREQUENCY(x) ((x)<<25) +#define HSW_FREQUENCY(x) ((x)<<24) #define GEN6_OFFSET(x) ((x)<<19) #define GEN6_AGGRESSIVE_TURBO (0<<15) #define GEN6_RC_VIDEO_FREQ 0xA00C @@ -4275,6 +4381,20 @@ #define GEN6_PCODE_DATA 0x138128 #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 +#define VLV_IOSF_DOORBELL_REQ 0x182100 +#define IOSF_DEVFN_SHIFT 24 +#define IOSF_OPCODE_SHIFT 16 +#define IOSF_PORT_SHIFT 8 +#define IOSF_BYTE_ENABLES_SHIFT 4 +#define IOSF_BAR_SHIFT 1 +#define IOSF_SB_BUSY (1<<0) +#define IOSF_PORT_PUNIT 0x4 +#define VLV_IOSF_DATA 0x182104 +#define VLV_IOSF_ADDR 0x182108 + +#define PUNIT_OPCODE_REG_READ 6 +#define PUNIT_OPCODE_REG_WRITE 7 + #define GEN6_GT_CORE_STATUS 0x138060 #define GEN6_CORE_CPD_STATE_MASK (7<<4) #define GEN6_RCn_MASK 7 diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 2135f21..41f0fde 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -209,7 +209,8 @@ static void i915_save_display(struct drm_device *dev) dev_priv->regfile.saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2); dev_priv->regfile.saveBLC_CPU_PWM_CTL = I915_READ(BLC_PWM_CPU_CTL); dev_priv->regfile.saveBLC_CPU_PWM_CTL2 = I915_READ(BLC_PWM_CPU_CTL2); - dev_priv->regfile.saveLVDS = I915_READ(PCH_LVDS); + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + dev_priv->regfile.saveLVDS = I915_READ(PCH_LVDS); } else { dev_priv->regfile.savePP_CONTROL = I915_READ(PP_CONTROL); dev_priv->regfile.savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS); @@ -255,6 +256,7 @@ static void i915_save_display(struct drm_device *dev) static void i915_restore_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 mask = 0xffffffff; /* Display arbitration */ if (INTEL_INFO(dev)->gen <= 4) @@ -267,10 +269,13 @@ static void i915_restore_display(struct drm_device *dev) if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) I915_WRITE(BLC_PWM_CTL2, dev_priv->regfile.saveBLC_PWM_CTL2); - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_LVDS, dev_priv->regfile.saveLVDS); - } else if (IS_MOBILE(dev) && !IS_I830(dev)) - I915_WRITE(LVDS, dev_priv->regfile.saveLVDS); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + mask = ~LVDS_PORT_EN; + + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + I915_WRITE(PCH_LVDS, dev_priv->regfile.saveLVDS & mask); + else if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev)) + I915_WRITE(LVDS, dev_priv->regfile.saveLVDS & mask); if (!IS_I830(dev) && !IS_845G(dev) && !HAS_PCH_SPLIT(dev)) I915_WRITE(PFIT_CONTROL, dev_priv->regfile.savePFIT_CONTROL); diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 9462081..a3a3e22 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -49,7 +49,7 @@ static ssize_t show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf) { struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev); - return snprintf(buf, PAGE_SIZE, "%x", intel_enable_rc6(dminor->dev)); + return snprintf(buf, PAGE_SIZE, "%x\n", intel_enable_rc6(dminor->dev)); } static ssize_t @@ -57,7 +57,7 @@ show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf) { struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev); u32 rc6_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6); - return snprintf(buf, PAGE_SIZE, "%u", rc6_residency); + return snprintf(buf, PAGE_SIZE, "%u\n", rc6_residency); } static ssize_t @@ -65,7 +65,7 @@ show_rc6p_ms(struct device *kdev, struct device_attribute *attr, char *buf) { struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev); u32 rc6p_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6p); - return snprintf(buf, PAGE_SIZE, "%u", rc6p_residency); + return snprintf(buf, PAGE_SIZE, "%u\n", rc6p_residency); } static ssize_t @@ -73,7 +73,7 @@ show_rc6pp_ms(struct device *kdev, struct device_attribute *attr, char *buf) { struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev); u32 rc6pp_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6pp); - return snprintf(buf, PAGE_SIZE, "%u", rc6pp_residency); + return snprintf(buf, PAGE_SIZE, "%u\n", rc6pp_residency); } static DEVICE_ATTR(rc6_enable, S_IRUGO, show_rc6_mask, NULL); @@ -215,7 +215,7 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); - return snprintf(buf, PAGE_SIZE, "%d", ret); + return snprintf(buf, PAGE_SIZE, "%d\n", ret); } static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) @@ -229,7 +229,7 @@ static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); - return snprintf(buf, PAGE_SIZE, "%d", ret); + return snprintf(buf, PAGE_SIZE, "%d\n", ret); } static ssize_t gt_max_freq_mhz_store(struct device *kdev, @@ -280,7 +280,7 @@ static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); - return snprintf(buf, PAGE_SIZE, "%d", ret); + return snprintf(buf, PAGE_SIZE, "%d\n", ret); } static ssize_t gt_min_freq_mhz_store(struct device *kdev, @@ -355,7 +355,7 @@ static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr } else { BUG(); } - return snprintf(buf, PAGE_SIZE, "%d", val); + return snprintf(buf, PAGE_SIZE, "%d\n", val); } static const struct attribute *gen6_attrs[] = { diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 969d08c..1d8d63a 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -45,6 +45,9 @@ struct intel_crt { struct intel_encoder base; + /* DPMS state is stored in the connector, which we need in the + * encoder's enable/disable callbacks */ + struct intel_connector *connector; bool force_hotplug_required; u32 adpa_reg; }; @@ -81,29 +84,6 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder, return true; } -static void intel_disable_crt(struct intel_encoder *encoder) -{ - struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; - struct intel_crt *crt = intel_encoder_to_crt(encoder); - u32 temp; - - temp = I915_READ(crt->adpa_reg); - temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); - temp &= ~ADPA_DAC_ENABLE; - I915_WRITE(crt->adpa_reg, temp); -} - -static void intel_enable_crt(struct intel_encoder *encoder) -{ - struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; - struct intel_crt *crt = intel_encoder_to_crt(encoder); - u32 temp; - - temp = I915_READ(crt->adpa_reg); - temp |= ADPA_DAC_ENABLE; - I915_WRITE(crt->adpa_reg, temp); -} - /* Note: The caller is required to filter out dpms modes not supported by the * platform. */ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) @@ -135,6 +115,19 @@ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) I915_WRITE(crt->adpa_reg, temp); } +static void intel_disable_crt(struct intel_encoder *encoder) +{ + intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void intel_enable_crt(struct intel_encoder *encoder) +{ + struct intel_crt *crt = intel_encoder_to_crt(encoder); + + intel_crt_set_dpms(encoder, crt->connector->base.dpms); +} + + static void intel_crt_dpms(struct drm_connector *connector, int mode) { struct drm_device *dev = connector->dev; @@ -206,10 +199,14 @@ static int intel_crt_mode_valid(struct drm_connector *connector, return MODE_OK; } -static bool intel_crt_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool intel_crt_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { + struct drm_device *dev = encoder->base.dev; + + if (HAS_PCH_SPLIT(dev)) + pipe_config->has_pch_encoder = true; + return true; } @@ -683,7 +680,6 @@ static void intel_crt_reset(struct drm_connector *connector) */ static const struct drm_encoder_helper_funcs crt_encoder_funcs = { - .mode_fixup = intel_crt_mode_fixup, .mode_set = intel_crt_mode_set, }; @@ -746,6 +742,7 @@ void intel_crt_init(struct drm_device *dev) } connector = &intel_connector->base; + crt->connector = intel_connector; drm_connector_init(dev, &intel_connector->base, &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); @@ -774,8 +771,11 @@ void intel_crt_init(struct drm_device *dev) else crt->adpa_reg = ADPA; + crt->base.compute_config = intel_crt_compute_config; crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; + if (I915_HAS_HOTPLUG(dev)) + crt->base.hpd_pin = HPD_CRT; if (HAS_DDI(dev)) crt->base.get_hw_state = intel_ddi_get_hw_state; else @@ -797,8 +797,6 @@ void intel_crt_init(struct drm_device *dev) */ crt->force_hotplug_required = 0; - dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS; - /* * TODO: find a proper way to discover whether we need to set the the * polarity and link reversal bits or not, instead of relying on the diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index d64af5a..22524cb 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -898,6 +898,9 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) plls->spll_refcount++; reg = SPLL_CTL; intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL; + } else { + DRM_ERROR("SPLL already in use\n"); + return false; } WARN(I915_READ(reg) & SPLL_PLL_ENABLE, @@ -928,7 +931,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { temp = TRANS_MSA_SYNC_CLK; - switch (intel_crtc->bpp) { + switch (intel_crtc->config.pipe_bpp) { case 18: temp |= TRANS_MSA_6_BPC; break; @@ -942,15 +945,13 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) temp |= TRANS_MSA_12_BPC; break; default: - temp |= TRANS_MSA_8_BPC; - WARN(1, "%d bpp unsupported by DDI function\n", - intel_crtc->bpp); + BUG(); } I915_WRITE(TRANS_MSA_MISC(cpu_transcoder), temp); } } -void intel_ddi_enable_pipe_func(struct drm_crtc *crtc) +void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); @@ -966,7 +967,7 @@ void intel_ddi_enable_pipe_func(struct drm_crtc *crtc) temp = TRANS_DDI_FUNC_ENABLE; temp |= TRANS_DDI_SELECT_PORT(port); - switch (intel_crtc->bpp) { + switch (intel_crtc->config.pipe_bpp) { case 18: temp |= TRANS_DDI_BPC_6; break; @@ -980,8 +981,7 @@ void intel_ddi_enable_pipe_func(struct drm_crtc *crtc) temp |= TRANS_DDI_BPC_12; break; default: - WARN(1, "%d bpp unsupported by transcoder DDI function\n", - intel_crtc->bpp); + BUG(); } if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC) @@ -1150,14 +1150,14 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, DRM_DEBUG_KMS("No pipe for ddi port %i found\n", port); - return true; + return false; } static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv, enum pipe pipe) { uint32_t temp, ret; - enum port port; + enum port port = I915_MAX_PORTS; enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); int i; @@ -1173,10 +1173,16 @@ static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv, port = i; } - ret = I915_READ(PORT_CLK_SEL(port)); - - DRM_DEBUG_KMS("Pipe %c connected to port %c using clock 0x%08x\n", - pipe_name(pipe), port_name(port), ret); + if (port == I915_MAX_PORTS) { + WARN(1, "Pipe %c enabled on an unknown port\n", + pipe_name(pipe)); + ret = PORT_CLK_SEL_NONE; + } else { + ret = I915_READ(PORT_CLK_SEL(port)); + DRM_DEBUG_KMS("Pipe %c connected to port %c using clock " + "0x%08x\n", pipe_name(pipe), port_name(port), + ret); + } return ret; } @@ -1341,15 +1347,15 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t tmp; + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); ironlake_edp_backlight_off(intel_dp); } - - tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); - tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); - I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); } int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) @@ -1391,8 +1397,8 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder) struct intel_dp *intel_dp = &intel_dig_port->dp; struct drm_i915_private *dev_priv = encoder->dev->dev_private; enum port port = intel_dig_port->port; - bool wait; uint32_t val; + bool wait = false; if (I915_READ(DP_TP_CTL(port)) & DP_TP_CTL_ENABLE) { val = I915_READ(DDI_BUF_CTL(port)); @@ -1467,19 +1473,17 @@ static void intel_ddi_destroy(struct drm_encoder *encoder) intel_dp_encoder_destroy(encoder); } -static bool intel_ddi_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool intel_ddi_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { - struct intel_encoder *intel_encoder = to_intel_encoder(encoder); - int type = intel_encoder->type; + int type = encoder->type; - WARN(type == INTEL_OUTPUT_UNKNOWN, "mode_fixup() on unknown output!\n"); + WARN(type == INTEL_OUTPUT_UNKNOWN, "compute_config() on unknown output!\n"); if (type == INTEL_OUTPUT_HDMI) - return intel_hdmi_mode_fixup(encoder, mode, adjusted_mode); + return intel_hdmi_compute_config(encoder, pipe_config); else - return intel_dp_mode_fixup(encoder, mode, adjusted_mode); + return intel_dp_compute_config(encoder, pipe_config); } static const struct drm_encoder_funcs intel_ddi_funcs = { @@ -1487,7 +1491,6 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { }; static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = { - .mode_fixup = intel_ddi_mode_fixup, .mode_set = intel_ddi_mode_set, }; @@ -1527,6 +1530,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(encoder, &intel_ddi_helper_funcs); + intel_encoder->compute_config = intel_ddi_compute_config; intel_encoder->enable = intel_enable_ddi; intel_encoder->pre_enable = intel_ddi_pre_enable; intel_encoder->disable = intel_disable_ddi; @@ -1537,9 +1541,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) & DDI_BUF_PORT_REVERSAL; if (hdmi_connector) - intel_dig_port->hdmi.sdvox_reg = DDI_BUF_CTL(port); - else - intel_dig_port->hdmi.sdvox_reg = 0; + intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port); intel_dig_port->dp.output_reg = DDI_BUF_CTL(port); intel_encoder->type = INTEL_OUTPUT_UNKNOWN; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a05ac2c..b700564 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -71,8 +71,24 @@ typedef struct intel_limit intel_limit_t; struct intel_limit { intel_range_t dot, vco, n, m, m1, m2, p, p1; intel_p2_t p2; - bool (* find_pll)(const intel_limit_t *, struct drm_crtc *, - int, int, intel_clock_t *, intel_clock_t *); + /** + * find_pll() - Find the best values for the PLL + * @limit: limits for the PLL + * @crtc: current CRTC + * @target: target frequency in kHz + * @refclk: reference clock frequency in kHz + * @match_clock: if provided, @best_clock P divider must + * match the P divider from @match_clock + * used for LVDS downclocking + * @best_clock: best PLL values found + * + * Returns true on success, false on failure. + */ + bool (*find_pll)(const intel_limit_t *limit, + struct drm_crtc *crtc, + int target, int refclk, + intel_clock_t *match_clock, + intel_clock_t *best_clock); }; /* FDI */ @@ -471,7 +487,6 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { if (intel_is_dual_link_lvds(dev)) { - /* LVDS dual channel */ if (refclk == 100000) limit = &intel_limits_ironlake_dual_lvds_100m; else @@ -498,10 +513,8 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { if (intel_is_dual_link_lvds(dev)) - /* LVDS with dual channel */ limit = &intel_limits_g4x_dual_channel_lvds; else - /* LVDS with dual channel */ limit = &intel_limits_g4x_single_channel_lvds; } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) || intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) { @@ -1254,7 +1267,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, int cur_pipe; /* Planes are fixed to pipes on ILK+ */ - if (HAS_PCH_SPLIT(dev_priv->dev)) { + if (HAS_PCH_SPLIT(dev_priv->dev) || IS_VALLEYVIEW(dev_priv->dev)) { reg = DSPCNTR(pipe); val = I915_READ(reg); WARN((val & DISPLAY_PLANE_ENABLE), @@ -1275,6 +1288,25 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, } } +static void assert_sprites_disabled(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + int reg, i; + u32 val; + + if (!IS_VALLEYVIEW(dev_priv->dev)) + return; + + /* Need to check both planes against the pipe */ + for (i = 0; i < dev_priv->num_plane; i++) { + reg = SPCNTR(pipe, i); + val = I915_READ(reg); + WARN((val & SP_ENABLE), + "sprite %d assertion failure, should be off on pipe %c but is still active\n", + pipe * 2 + i, pipe_name(pipe)); + } +} + static void assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) { u32 val; @@ -1327,14 +1359,14 @@ static bool dp_pipe_enabled(struct drm_i915_private *dev_priv, static bool hdmi_pipe_enabled(struct drm_i915_private *dev_priv, enum pipe pipe, u32 val) { - if ((val & PORT_ENABLE) == 0) + if ((val & SDVO_ENABLE) == 0) return false; if (HAS_PCH_CPT(dev_priv->dev)) { - if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe)) + if ((val & SDVO_PIPE_SEL_MASK_CPT) != SDVO_PIPE_SEL_CPT(pipe)) return false; } else { - if ((val & TRANSCODER_MASK) != TRANSCODER(pipe)) + if ((val & SDVO_PIPE_SEL_MASK) != SDVO_PIPE_SEL(pipe)) return false; } return true; @@ -1392,7 +1424,7 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv, "PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n", reg, pipe_name(pipe)); - WARN(HAS_PCH_IBX(dev_priv->dev) && (val & PORT_ENABLE) == 0 + WARN(HAS_PCH_IBX(dev_priv->dev) && (val & SDVO_ENABLE) == 0 && (val & SDVO_PIPE_B_SELECT), "IBX PCH hdmi port still using transcoder B\n"); } @@ -1419,9 +1451,9 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, "PCH LVDS enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); - assert_pch_hdmi_disabled(dev_priv, pipe, HDMIB); - assert_pch_hdmi_disabled(dev_priv, pipe, HDMIC); - assert_pch_hdmi_disabled(dev_priv, pipe, HDMID); + assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMIB); + assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMIC); + assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMID); } /** @@ -1859,6 +1891,7 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv, * or we might hang the display. */ assert_planes_disabled(dev_priv, pipe); + assert_sprites_disabled(dev_priv, pipe); /* Don't disable pipe A or pipe A PLLs if needed */ if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) @@ -1937,6 +1970,15 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv, intel_wait_for_vblank(dev_priv->dev, pipe); } +static bool need_vtd_wa(struct drm_device *dev) +{ +#ifdef CONFIG_INTEL_IOMMU + if (INTEL_INFO(dev)->gen >= 6 && intel_iommu_gfx_mapped) + return true; +#endif + return false; +} + int intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_i915_gem_object *obj, @@ -1967,6 +2009,14 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, BUG(); } + /* Note that the w/a also requires 64 PTE of padding following the + * bo. We currently fill all unused PTE with the shadow page and so + * we should always have valid PTE following the scanout preventing + * the VT-d warning. + */ + if (need_vtd_wa(dev) && alignment < 256 * 1024) + alignment = 256 * 1024; + dev_priv->mm.interruptible = false; ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined); if (ret) @@ -2083,8 +2133,7 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr |= DISPPLANE_RGBX101010; break; default: - DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format); - return -EINVAL; + BUG(); } if (INTEL_INFO(dev)->gen >= 4) { @@ -2177,8 +2226,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc, dspcntr |= DISPPLANE_RGBX101010; break; default: - DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format); - return -EINVAL; + BUG(); } if (obj->tiling_mode != I915_TILING_NONE) @@ -2229,6 +2277,44 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, return dev_priv->display.update_plane(crtc, fb, x, y); } +void intel_display_handle_reset(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + + /* + * Flips in the rings have been nuked by the reset, + * so complete all pending flips so that user space + * will get its events and not get stuck. + * + * Also update the base address of all primary + * planes to the the last fb to make sure we're + * showing the correct fb after a reset. + * + * Need to make two loops over the crtcs so that we + * don't try to grab a crtc mutex before the + * pending_flip_queue really got woken up. + */ + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum plane plane = intel_crtc->plane; + + intel_prepare_page_flip(dev, plane); + intel_finish_page_flip_plane(dev, plane); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + mutex_lock(&crtc->mutex); + if (intel_crtc->active) + dev_priv->display.update_plane(crtc, crtc->fb, + crtc->x, crtc->y); + mutex_unlock(&crtc->mutex); + } +} + static int intel_finish_fb(struct drm_framebuffer *old_fb) { @@ -2295,10 +2381,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } - if(intel_crtc->plane > dev_priv->num_pipe) { + if (intel_crtc->plane > INTEL_INFO(dev)->num_pipes) { DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n", intel_crtc->plane, - dev_priv->num_pipe); + INTEL_INFO(dev)->num_pipes); return -EINVAL; } @@ -2312,9 +2398,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return ret; } - if (crtc->fb) - intel_finish_fb(crtc->fb); - ret = dev_priv->display.update_plane(crtc, fb, x, y); if (ret) { intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj); @@ -2912,32 +2995,6 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) mutex_unlock(&dev->struct_mutex); } -static bool ironlake_crtc_driving_pch(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct intel_encoder *intel_encoder; - - /* - * If there's a non-PCH eDP on this crtc, it must be DP_A, and that - * must be driven by its own crtc; no sharing is possible. - */ - for_each_encoder_on_crtc(dev, crtc, intel_encoder) { - switch (intel_encoder->type) { - case INTEL_OUTPUT_EDP: - if (!intel_encoder_is_pch_edp(&intel_encoder->base)) - return false; - continue; - } - } - - return true; -} - -static bool haswell_crtc_driving_pch(struct drm_crtc *crtc) -{ - return intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG); -} - /* Program iCLKIP clock to the desired frequency */ static void lpt_program_iclkip(struct drm_crtc *crtc) { @@ -3273,7 +3330,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; u32 temp; - bool is_pch_port; WARN_ON(!crtc->enabled); @@ -3289,9 +3345,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN); } - is_pch_port = ironlake_crtc_driving_pch(crtc); - if (is_pch_port) { + if (intel_crtc->config.has_pch_encoder) { /* Note: FDI PLL enabling _must_ be done before we enable the * cpu pipes, hence this is separate from all the other fdi/pch * enabling. */ @@ -3328,10 +3383,11 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) */ intel_crtc_load_lut(crtc); - intel_enable_pipe(dev_priv, pipe, is_pch_port); + intel_enable_pipe(dev_priv, pipe, + intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); - if (is_pch_port) + if (intel_crtc->config.has_pch_encoder) ironlake_pch_enable(crtc); mutex_lock(&dev->struct_mutex); @@ -3365,7 +3421,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) struct intel_encoder *encoder; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; - bool is_pch_port; WARN_ON(!crtc->enabled); @@ -3375,9 +3430,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_crtc->active = true; intel_update_watermarks(dev); - is_pch_port = haswell_crtc_driving_pch(crtc); - - if (is_pch_port) + if (intel_crtc->config.has_pch_encoder) dev_priv->display.fdi_link_train(crtc); for_each_encoder_on_crtc(dev, crtc, encoder) @@ -3406,12 +3459,13 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_crtc_load_lut(crtc); intel_ddi_set_pipe_settings(crtc); - intel_ddi_enable_pipe_func(crtc); + intel_ddi_enable_transcoder_func(crtc); - intel_enable_pipe(dev_priv, pipe, is_pch_port); + intel_enable_pipe(dev_priv, pipe, + intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); - if (is_pch_port) + if (intel_crtc->config.has_pch_encoder) lpt_pch_enable(crtc); mutex_lock(&dev->struct_mutex); @@ -3523,13 +3577,10 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; - bool is_pch_port; if (!intel_crtc->active) return; - is_pch_port = haswell_crtc_driving_pch(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) encoder->disable(encoder); @@ -3556,7 +3607,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (encoder->post_disable) encoder->post_disable(encoder); - if (is_pch_port) { + if (intel_crtc->config.has_pch_encoder) { lpt_disable_pch_transcoder(dev_priv); intel_ddi_fdi_disable(crtc); } @@ -3604,6 +3655,30 @@ static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) */ } +/** + * i9xx_fixup_plane - ugly workaround for G45 to fire up the hardware + * cursor plane briefly if not already running after enabling the display + * plane. + * This workaround avoids occasional blank screens when self refresh is + * enabled. + */ +static void +g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe) +{ + u32 cntl = I915_READ(CURCNTR(pipe)); + + if ((cntl & CURSOR_MODE) == 0) { + u32 fw_bcl_self = I915_READ(FW_BLC_SELF); + + I915_WRITE(FW_BLC_SELF, fw_bcl_self & ~FW_BLC_SELF_EN); + I915_WRITE(CURCNTR(pipe), CURSOR_MODE_64_ARGB_AX); + intel_wait_for_vblank(dev_priv->dev, pipe); + I915_WRITE(CURCNTR(pipe), cntl); + I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe))); + I915_WRITE(FW_BLC_SELF, fw_bcl_self); + } +} + static void i9xx_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3629,6 +3704,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); + if (IS_G4X(dev)) + g4x_fixup_plane(dev_priv, pipe); intel_crtc_load_lut(crtc); intel_update_fbc(dev); @@ -3880,22 +3957,23 @@ bool intel_connector_get_hw_state(struct intel_connector *connector) return encoder->get_hw_state(encoder, &pipe); } -static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool intel_crtc_compute_config(struct drm_crtc *crtc, + struct intel_crtc_config *pipe_config) { struct drm_device *dev = crtc->dev; + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; if (HAS_PCH_SPLIT(dev)) { /* FDI link clock is fixed at 2.7G */ - if (mode->clock * 3 > IRONLAKE_FDI_FREQ * 4) + if (pipe_config->requested_mode.clock * 3 + > IRONLAKE_FDI_FREQ * 4) return false; } /* All interlaced capable intel hw wants timings in frames. Note though * that intel_lvds_mode_fixup does some funny tricks with the crtc * timings, so we need to be careful not to clobber these.*/ - if (!(adjusted_mode->private_flags & INTEL_MODE_CRTC_TIMINGS_SET)) + if (!pipe_config->timings_set) drm_mode_set_crtcinfo(adjusted_mode, 0); /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes @@ -3905,6 +3983,14 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, adjusted_mode->hsync_start == adjusted_mode->hdisplay) return false; + if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && pipe_config->pipe_bpp > 10) { + pipe_config->pipe_bpp = 10*3; /* 12bpc is gen5+ */ + } else if (INTEL_INFO(dev)->gen <= 4 && pipe_config->pipe_bpp > 8) { + /* only a 8bpc pipe, with 6bpc dither through the panel fitter + * for lvds. */ + pipe_config->pipe_bpp = 8*3; + } + return true; } @@ -4008,142 +4094,6 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); } -/** - * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should send - * @crtc: CRTC structure - * @mode: requested mode - * - * A pipe may be connected to one or more outputs. Based on the depth of the - * attached framebuffer, choose a good color depth to use on the pipe. - * - * If possible, match the pipe depth to the fb depth. In some cases, this - * isn't ideal, because the connected output supports a lesser or restricted - * set of depths. Resolve that here: - * LVDS typically supports only 6bpc, so clamp down in that case - * HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc - * Displays may support a restricted set as well, check EDID and clamp as - * appropriate. - * DP may want to dither down to 6bpc to fit larger modes - * - * RETURNS: - * Dithering requirement (i.e. false if display bpc and pipe bpc match, - * true if they don't match). - */ -static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - unsigned int *pipe_bpp, - struct drm_display_mode *mode) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_connector *connector; - struct intel_encoder *intel_encoder; - unsigned int display_bpc = UINT_MAX, bpc; - - /* Walk the encoders & connectors on this crtc, get min bpc */ - for_each_encoder_on_crtc(dev, crtc, intel_encoder) { - - if (intel_encoder->type == INTEL_OUTPUT_LVDS) { - unsigned int lvds_bpc; - - if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == - LVDS_A3_POWER_UP) - lvds_bpc = 8; - else - lvds_bpc = 6; - - if (lvds_bpc < display_bpc) { - DRM_DEBUG_KMS("clamping display bpc (was %d) to LVDS (%d)\n", display_bpc, lvds_bpc); - display_bpc = lvds_bpc; - } - continue; - } - - /* Not one of the known troublemakers, check the EDID */ - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { - if (connector->encoder != &intel_encoder->base) - continue; - - /* Don't use an invalid EDID bpc value */ - if (connector->display_info.bpc && - connector->display_info.bpc < display_bpc) { - DRM_DEBUG_KMS("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc); - display_bpc = connector->display_info.bpc; - } - } - - if (intel_encoder->type == INTEL_OUTPUT_EDP) { - /* Use VBT settings if we have an eDP panel */ - unsigned int edp_bpc = dev_priv->edp.bpp / 3; - - if (edp_bpc && edp_bpc < display_bpc) { - DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc); - display_bpc = edp_bpc; - } - continue; - } - - /* - * HDMI is either 12 or 8, so if the display lets 10bpc sneak - * through, clamp it down. (Note: >12bpc will be caught below.) - */ - if (intel_encoder->type == INTEL_OUTPUT_HDMI) { - if (display_bpc > 8 && display_bpc < 12) { - DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n"); - display_bpc = 12; - } else { - DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n"); - display_bpc = 8; - } - } - } - - if (mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - DRM_DEBUG_KMS("Dithering DP to 6bpc\n"); - display_bpc = 6; - } - - /* - * We could just drive the pipe at the highest bpc all the time and - * enable dithering as needed, but that costs bandwidth. So choose - * the minimum value that expresses the full color range of the fb but - * also stays within the max display bpc discovered above. - */ - - switch (fb->depth) { - case 8: - bpc = 8; /* since we go through a colormap */ - break; - case 15: - case 16: - bpc = 6; /* min is 18bpp */ - break; - case 24: - bpc = 8; - break; - case 30: - bpc = 10; - break; - case 48: - bpc = 12; - break; - default: - DRM_DEBUG("unsupported depth, assuming 24 bits\n"); - bpc = min((unsigned int)8, display_bpc); - break; - } - - display_bpc = min(display_bpc, bpc); - - DRM_DEBUG_KMS("setting pipe bpc to %d (max display bpc %d)\n", - bpc, display_bpc); - - *pipe_bpp = display_bpc * 3; - - return display_bpc != bpc; -} - static int vlv_get_refclk(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -4188,37 +4138,38 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) return refclk; } -static void i9xx_adjust_sdvo_tv_clock(struct drm_display_mode *adjusted_mode, - intel_clock_t *clock) +static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc *crtc) { + unsigned dotclock = crtc->config.adjusted_mode.clock; + struct dpll *clock = &crtc->config.dpll; + /* SDVO TV has fixed PLL values depend on its clock range, this mirrors vbios setting. */ - if (adjusted_mode->clock >= 100000 - && adjusted_mode->clock < 140500) { + if (dotclock >= 100000 && dotclock < 140500) { clock->p1 = 2; clock->p2 = 10; clock->n = 3; clock->m1 = 16; clock->m2 = 8; - } else if (adjusted_mode->clock >= 140500 - && adjusted_mode->clock <= 200000) { + } else if (dotclock >= 140500 && dotclock <= 200000) { clock->p1 = 1; clock->p2 = 10; clock->n = 6; clock->m1 = 12; clock->m2 = 8; } + + crtc->config.clock_set = true; } -static void i9xx_update_pll_dividers(struct drm_crtc *crtc, - intel_clock_t *clock, +static void i9xx_update_pll_dividers(struct intel_crtc *crtc, intel_clock_t *reduced_clock) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; + int pipe = crtc->pipe; u32 fp, fp2 = 0; + struct dpll *clock = &crtc->config.dpll; if (IS_PINEVIEW(dev)) { fp = (1 << clock->n) << 16 | clock->m1 << 8 | clock->m2; @@ -4234,26 +4185,29 @@ static void i9xx_update_pll_dividers(struct drm_crtc *crtc, I915_WRITE(FP0(pipe), fp); - intel_crtc->lowfreq_avail = false; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + crtc->lowfreq_avail = false; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && reduced_clock && i915_powersave) { I915_WRITE(FP1(pipe), fp2); - intel_crtc->lowfreq_avail = true; + crtc->lowfreq_avail = true; } else { I915_WRITE(FP1(pipe), fp); } } -static void vlv_update_pll(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - intel_clock_t *clock, intel_clock_t *reduced_clock, - int num_connectors) +static void intel_dp_set_m_n(struct intel_crtc *crtc) { - struct drm_device *dev = crtc->dev; + if (crtc->config.has_pch_encoder) + intel_pch_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); + else + intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); +} + +static void vlv_update_pll(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; + int pipe = crtc->pipe; u32 dpll, mdiv, pdiv; u32 bestn, bestm1, bestm2, bestp1, bestp2; bool is_sdvo; @@ -4261,8 +4215,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, mutex_lock(&dev_priv->dpio_lock); - is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) || - intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); + is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); dpll = DPLL_VGA_MODE_DIS; dpll |= DPLL_EXT_BUFFER_ENABLE_VLV; @@ -4272,11 +4226,11 @@ static void vlv_update_pll(struct drm_crtc *crtc, I915_WRITE(DPLL(pipe), dpll); POSTING_READ(DPLL(pipe)); - bestn = clock->n; - bestm1 = clock->m1; - bestm2 = clock->m2; - bestp1 = clock->p1; - bestp2 = clock->p2; + bestn = crtc->config.dpll.n; + bestm1 = crtc->config.dpll.m1; + bestm2 = crtc->config.dpll.m2; + bestp1 = crtc->config.dpll.p1; + bestp2 = crtc->config.dpll.p2; /* * In Valleyview PLL and program lane counter registers are exposed @@ -4308,8 +4262,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x620); - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) - intel_dp_set_m_n(crtc, mode, adjusted_mode); + if (crtc->config.has_dp_encoder) + intel_dp_set_m_n(crtc); I915_WRITE(DPLL(pipe), dpll); @@ -4319,26 +4273,25 @@ static void vlv_update_pll(struct drm_crtc *crtc, temp = 0; if (is_sdvo) { - temp = intel_mode_get_pixel_multiplier(adjusted_mode); - if (temp > 1) - temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; - else - temp = 0; + temp = 0; + if (crtc->config.pixel_multiplier > 1) { + temp = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; + } } I915_WRITE(DPLL_MD(pipe), temp); POSTING_READ(DPLL_MD(pipe)); /* Now program lane control registers */ - if(intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) - || intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) - { + if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) + || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) { temp = 0x1000C4; if(pipe == 1) temp |= (1 << 21); intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL1, temp); } - if(intel_pipe_has_type(crtc,INTEL_OUTPUT_EDP)) - { + + if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) { temp = 0x1000C4; if(pipe == 1) temp |= (1 << 21); @@ -4348,40 +4301,39 @@ static void vlv_update_pll(struct drm_crtc *crtc, mutex_unlock(&dev_priv->dpio_lock); } -static void i9xx_update_pll(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - intel_clock_t *clock, intel_clock_t *reduced_clock, +static void i9xx_update_pll(struct intel_crtc *crtc, + intel_clock_t *reduced_clock, int num_connectors) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; - int pipe = intel_crtc->pipe; + int pipe = crtc->pipe; u32 dpll; bool is_sdvo; + struct dpll *clock = &crtc->config.dpll; - i9xx_update_pll_dividers(crtc, clock, reduced_clock); + i9xx_update_pll_dividers(crtc, reduced_clock); - is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) || - intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); + is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); dpll = DPLL_VGA_MODE_DIS; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)) dpll |= DPLLB_MODE_LVDS; else dpll |= DPLLB_MODE_DAC_SERIAL; + if (is_sdvo) { - int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); - if (pixel_multiplier > 1) { - if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) - dpll |= (pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + if ((crtc->config.pixel_multiplier > 1) && + (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) { + dpll |= (crtc->config.pixel_multiplier - 1) + << SDVO_MULTIPLIER_SHIFT_HIRES; } dpll |= DPLL_DVO_HIGH_SPEED; } - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) dpll |= DPLL_DVO_HIGH_SPEED; /* compute bitmask from p1 value */ @@ -4409,13 +4361,13 @@ static void i9xx_update_pll(struct drm_crtc *crtc, if (INTEL_INFO(dev)->gen >= 4) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); - if (is_sdvo && intel_pipe_has_type(crtc, INTEL_OUTPUT_TVOUT)) + if (is_sdvo && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_TVOUT)) + else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) /* XXX: just matching BIOS for now */ /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; - else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; else @@ -4426,12 +4378,12 @@ static void i9xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); - for_each_encoder_on_crtc(dev, crtc, encoder) + for_each_encoder_on_crtc(dev, &crtc->base, encoder) if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) - intel_dp_set_m_n(crtc, mode, adjusted_mode); + if (crtc->config.has_dp_encoder) + intel_dp_set_m_n(crtc); I915_WRITE(DPLL(pipe), dpll); @@ -4442,11 +4394,11 @@ static void i9xx_update_pll(struct drm_crtc *crtc, if (INTEL_INFO(dev)->gen >= 4) { u32 temp = 0; if (is_sdvo) { - temp = intel_mode_get_pixel_multiplier(adjusted_mode); - if (temp > 1) - temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; - else - temp = 0; + temp = 0; + if (crtc->config.pixel_multiplier > 1) { + temp = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; + } } I915_WRITE(DPLL_MD(pipe), temp); } else { @@ -4459,23 +4411,23 @@ static void i9xx_update_pll(struct drm_crtc *crtc, } } -static void i8xx_update_pll(struct drm_crtc *crtc, +static void i8xx_update_pll(struct intel_crtc *crtc, struct drm_display_mode *adjusted_mode, - intel_clock_t *clock, intel_clock_t *reduced_clock, + intel_clock_t *reduced_clock, int num_connectors) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; - int pipe = intel_crtc->pipe; + int pipe = crtc->pipe; u32 dpll; + struct dpll *clock = &crtc->config.dpll; - i9xx_update_pll_dividers(crtc, clock, reduced_clock); + i9xx_update_pll_dividers(crtc, reduced_clock); dpll = DPLL_VGA_MODE_DIS; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)) { dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; } else { if (clock->p1 == 2) @@ -4486,11 +4438,7 @@ static void i8xx_update_pll(struct drm_crtc *crtc, dpll |= PLL_P2_DIVIDE_BY_4; } - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_TVOUT)) - /* XXX: just matching BIOS for now */ - /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; - else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; else @@ -4501,7 +4449,7 @@ static void i8xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); - for_each_encoder_on_crtc(dev, crtc, encoder) + for_each_encoder_on_crtc(dev, &crtc->base, encoder) if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); @@ -4577,22 +4525,92 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); } +static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t pipeconf; + + pipeconf = I915_READ(PIPECONF(intel_crtc->pipe)); + + if (intel_crtc->pipe == 0 && INTEL_INFO(dev)->gen < 4) { + /* Enable pixel doubling when the dot clock is > 90% of the (display) + * core speed. + * + * XXX: No double-wide on 915GM pipe B. Is that the only reason for the + * pipe == 0 check? + */ + if (intel_crtc->config.requested_mode.clock > + dev_priv->display.get_display_clock_speed(dev) * 9 / 10) + pipeconf |= PIPECONF_DOUBLE_WIDE; + else + pipeconf &= ~PIPECONF_DOUBLE_WIDE; + } + + /* default to 8bpc */ + pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); + if (intel_crtc->config.has_dp_encoder) { + if (intel_crtc->config.dither) { + pipeconf |= PIPECONF_6BPC | + PIPECONF_DITHER_EN | + PIPECONF_DITHER_TYPE_SP; + } + } + + if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(&intel_crtc->base, + INTEL_OUTPUT_EDP)) { + if (intel_crtc->config.dither) { + pipeconf |= PIPECONF_6BPC | + PIPECONF_ENABLE | + I965_PIPECONF_ACTIVE; + } + } + + if (HAS_PIPE_CXSR(dev)) { + if (intel_crtc->lowfreq_avail) { + DRM_DEBUG_KMS("enabling CxSR downclocking\n"); + pipeconf |= PIPECONF_CXSR_DOWNCLOCK; + } else { + DRM_DEBUG_KMS("disabling CxSR downclocking\n"); + pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; + } + } + + pipeconf &= ~PIPECONF_INTERLACE_MASK; + if (!IS_GEN2(dev) && + intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) + pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; + else + pipeconf |= PIPECONF_PROGRESSIVE; + + if (IS_VALLEYVIEW(dev)) { + if (intel_crtc->config.limited_color_range) + pipeconf |= PIPECONF_COLOR_RANGE_SELECT; + else + pipeconf &= ~PIPECONF_COLOR_RANGE_SELECT; + } + + I915_WRITE(PIPECONF(intel_crtc->pipe), pipeconf); + POSTING_READ(PIPECONF(intel_crtc->pipe)); +} + static int i9xx_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_display_mode *adjusted_mode = + &intel_crtc->config.adjusted_mode; + struct drm_display_mode *mode = &intel_crtc->config.requested_mode; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int refclk, num_connectors = 0; intel_clock_t clock, reduced_clock; - u32 dspcntr, pipeconf; + u32 dspcntr; bool ok, has_reduced_clock = false, is_sdvo = false; - bool is_lvds = false, is_tv = false, is_dp = false; + bool is_lvds = false, is_tv = false; struct intel_encoder *encoder; const intel_limit_t *limit; int ret; @@ -4611,9 +4629,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_TVOUT: is_tv = true; break; - case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; - break; } num_connectors++; @@ -4650,86 +4665,42 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, &clock, &reduced_clock); } + /* Compat-code for transition, will disappear. */ + if (!intel_crtc->config.clock_set) { + intel_crtc->config.dpll.n = clock.n; + intel_crtc->config.dpll.m1 = clock.m1; + intel_crtc->config.dpll.m2 = clock.m2; + intel_crtc->config.dpll.p1 = clock.p1; + intel_crtc->config.dpll.p2 = clock.p2; + } if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(adjusted_mode, &clock); + i9xx_adjust_sdvo_tv_clock(intel_crtc); if (IS_GEN2(dev)) - i8xx_update_pll(crtc, adjusted_mode, &clock, + i8xx_update_pll(intel_crtc, adjusted_mode, has_reduced_clock ? &reduced_clock : NULL, num_connectors); else if (IS_VALLEYVIEW(dev)) - vlv_update_pll(crtc, mode, adjusted_mode, &clock, - has_reduced_clock ? &reduced_clock : NULL, - num_connectors); + vlv_update_pll(intel_crtc); else - i9xx_update_pll(crtc, mode, adjusted_mode, &clock, + i9xx_update_pll(intel_crtc, has_reduced_clock ? &reduced_clock : NULL, num_connectors); - /* setup pipeconf */ - pipeconf = I915_READ(PIPECONF(pipe)); - /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; - if (pipe == 0) - dspcntr &= ~DISPPLANE_SEL_PIPE_MASK; - else - dspcntr |= DISPPLANE_SEL_PIPE_B; - - if (pipe == 0 && INTEL_INFO(dev)->gen < 4) { - /* Enable pixel doubling when the dot clock is > 90% of the (display) - * core speed. - * - * XXX: No double-wide on 915GM pipe B. Is that the only reason for the - * pipe == 0 check? - */ - if (mode->clock > - dev_priv->display.get_display_clock_speed(dev) * 9 / 10) - pipeconf |= PIPECONF_DOUBLE_WIDE; + if (!IS_VALLEYVIEW(dev)) { + if (pipe == 0) + dspcntr &= ~DISPPLANE_SEL_PIPE_MASK; else - pipeconf &= ~PIPECONF_DOUBLE_WIDE; - } - - /* default to 8bpc */ - pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); - if (is_dp) { - if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_6BPC | - PIPECONF_DITHER_EN | - PIPECONF_DITHER_TYPE_SP; - } - } - - if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { - if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_6BPC | - PIPECONF_ENABLE | - I965_PIPECONF_ACTIVE; - } + dspcntr |= DISPPLANE_SEL_PIPE_B; } DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); - if (HAS_PIPE_CXSR(dev)) { - if (intel_crtc->lowfreq_avail) { - DRM_DEBUG_KMS("enabling CxSR downclocking\n"); - pipeconf |= PIPECONF_CXSR_DOWNCLOCK; - } else { - DRM_DEBUG_KMS("disabling CxSR downclocking\n"); - pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; - } - } - - pipeconf &= ~PIPECONF_INTERLACE_MASK; - if (!IS_GEN2(dev) && - adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) - pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; - else - pipeconf |= PIPECONF_PROGRESSIVE; - intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); /* pipesrc and dspsize control the size that is scaled from, @@ -4740,8 +4711,8 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, (mode->hdisplay - 1)); I915_WRITE(DSPPOS(plane), 0); - I915_WRITE(PIPECONF(pipe), pipeconf); - POSTING_READ(PIPECONF(pipe)); + i9xx_set_pipeconf(intel_crtc); + intel_enable_pipe(dev_priv, pipe, false); intel_wait_for_vblank(dev, pipe); @@ -4756,12 +4727,26 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, return ret; } +static bool i9xx_get_pipe_config(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = I915_READ(PIPECONF(crtc->pipe)); + if (!(tmp & PIPECONF_ENABLE)) + return false; + + return true; +} + static void ironlake_init_pch_refclk(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; - u32 temp; + u32 val, final; bool has_lvds = false; bool has_cpu_edp = false; bool has_pch_edp = false; @@ -4804,70 +4789,109 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) * PCH B stepping, previous chipset stepping should be * ignoring this setting. */ - temp = I915_READ(PCH_DREF_CONTROL); + val = I915_READ(PCH_DREF_CONTROL); + + /* As we must carefully and slowly disable/enable each source in turn, + * compute the final state we want first and check if we need to + * make any changes at all. + */ + final = val; + final &= ~DREF_NONSPREAD_SOURCE_MASK; + if (has_ck505) + final |= DREF_NONSPREAD_CK505_ENABLE; + else + final |= DREF_NONSPREAD_SOURCE_ENABLE; + + final &= ~DREF_SSC_SOURCE_MASK; + final &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + final &= ~DREF_SSC1_ENABLE; + + if (has_panel) { + final |= DREF_SSC_SOURCE_ENABLE; + + if (intel_panel_use_ssc(dev_priv) && can_ssc) + final |= DREF_SSC1_ENABLE; + + if (has_cpu_edp) { + if (intel_panel_use_ssc(dev_priv) && can_ssc) + final |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; + else + final |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; + } else + final |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + } else { + final |= DREF_SSC_SOURCE_DISABLE; + final |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + } + + if (final == val) + return; + /* Always enable nonspread source */ - temp &= ~DREF_NONSPREAD_SOURCE_MASK; + val &= ~DREF_NONSPREAD_SOURCE_MASK; if (has_ck505) - temp |= DREF_NONSPREAD_CK505_ENABLE; + val |= DREF_NONSPREAD_CK505_ENABLE; else - temp |= DREF_NONSPREAD_SOURCE_ENABLE; + val |= DREF_NONSPREAD_SOURCE_ENABLE; if (has_panel) { - temp &= ~DREF_SSC_SOURCE_MASK; - temp |= DREF_SSC_SOURCE_ENABLE; + val &= ~DREF_SSC_SOURCE_MASK; + val |= DREF_SSC_SOURCE_ENABLE; /* SSC must be turned on before enabling the CPU output */ if (intel_panel_use_ssc(dev_priv) && can_ssc) { DRM_DEBUG_KMS("Using SSC on panel\n"); - temp |= DREF_SSC1_ENABLE; + val |= DREF_SSC1_ENABLE; } else - temp &= ~DREF_SSC1_ENABLE; + val &= ~DREF_SSC1_ENABLE; /* Get SSC going before enabling the outputs */ - I915_WRITE(PCH_DREF_CONTROL, temp); + I915_WRITE(PCH_DREF_CONTROL, val); POSTING_READ(PCH_DREF_CONTROL); udelay(200); - temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + val &= ~DREF_CPU_SOURCE_OUTPUT_MASK; /* Enable CPU source on CPU attached eDP */ if (has_cpu_edp) { if (intel_panel_use_ssc(dev_priv) && can_ssc) { DRM_DEBUG_KMS("Using SSC on eDP\n"); - temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; + val |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; } else - temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; + val |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; } else - temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + val |= DREF_CPU_SOURCE_OUTPUT_DISABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); + I915_WRITE(PCH_DREF_CONTROL, val); POSTING_READ(PCH_DREF_CONTROL); udelay(200); } else { DRM_DEBUG_KMS("Disabling SSC entirely\n"); - temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + val &= ~DREF_CPU_SOURCE_OUTPUT_MASK; /* Turn off CPU output */ - temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + val |= DREF_CPU_SOURCE_OUTPUT_DISABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); + I915_WRITE(PCH_DREF_CONTROL, val); POSTING_READ(PCH_DREF_CONTROL); udelay(200); /* Turn off the SSC source */ - temp &= ~DREF_SSC_SOURCE_MASK; - temp |= DREF_SSC_SOURCE_DISABLE; + val &= ~DREF_SSC_SOURCE_MASK; + val |= DREF_SSC_SOURCE_DISABLE; /* Turn off SSC1 */ - temp &= ~ DREF_SSC1_ENABLE; + val &= ~DREF_SSC1_ENABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); + I915_WRITE(PCH_DREF_CONTROL, val); POSTING_READ(PCH_DREF_CONTROL); udelay(200); } + + BUG_ON(val != final); } /* Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O. */ @@ -5092,7 +5116,7 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, val = I915_READ(PIPECONF(pipe)); val &= ~PIPECONF_BPC_MASK; - switch (intel_crtc->bpp) { + switch (intel_crtc->config.pipe_bpp) { case 18: val |= PIPECONF_6BPC; break; @@ -5120,7 +5144,7 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, else val |= PIPECONF_PROGRESSIVE; - if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + if (intel_crtc->config.limited_color_range) val |= PIPECONF_COLOR_RANGE_SELECT; else val &= ~PIPECONF_COLOR_RANGE_SELECT; @@ -5136,8 +5160,7 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, * is supported, but eventually this should handle various * RGB<->YCbCr scenarios as well. */ -static void intel_set_pipe_csc(struct drm_crtc *crtc, - const struct drm_display_mode *adjusted_mode) +static void intel_set_pipe_csc(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -5152,7 +5175,7 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc, * consideration. */ - if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + if (intel_crtc->config.limited_color_range) coeff = ((235 - 16) * (1 << 12) / 255) & 0xff8; /* 0.xxx... */ /* @@ -5176,7 +5199,7 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc, if (INTEL_INFO(dev)->gen > 6) { uint16_t postoff = 0; - if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + if (intel_crtc->config.limited_color_range) postoff = (16 * (1 << 13) / 255) & 0x1fff; I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff); @@ -5187,7 +5210,7 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc, } else { uint32_t mode = CSC_MODE_YUV_TO_RGB; - if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + if (intel_crtc->config.limited_color_range) mode |= CSC_BLACK_SCREEN_OFFSET; I915_WRITE(PIPE_CSC_MODE(pipe), mode); @@ -5277,7 +5300,7 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, } if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(adjusted_mode, clock); + i9xx_adjust_sdvo_tv_clock(to_intel_crtc(crtc)); return true; } @@ -5318,7 +5341,7 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) return false; } - if (dev_priv->num_pipe == 2) + if (INTEL_INFO(dev)->num_pipes == 2) return true; switch (intel_crtc->pipe) { @@ -5375,77 +5398,77 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp) return bps / (link_bw * 8) + 1; } -static void ironlake_set_m_n(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; - struct intel_encoder *intel_encoder, *edp_encoder = NULL; - struct intel_link_m_n m_n = {0}; - int target_clock, pixel_multiplier, lane, link_bw; - bool is_dp = false, is_cpu_edp = false; + int pipe = crtc->pipe; - for_each_encoder_on_crtc(dev, crtc, intel_encoder) { - switch (intel_encoder->type) { - case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; - break; - case INTEL_OUTPUT_EDP: - is_dp = true; - if (!intel_encoder_is_pch_edp(&intel_encoder->base)) - is_cpu_edp = true; - edp_encoder = intel_encoder; - break; - } - } + I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(TRANSDATA_N1(pipe), m_n->gmch_n); + I915_WRITE(TRANSDPLINK_M1(pipe), m_n->link_m); + I915_WRITE(TRANSDPLINK_N1(pipe), m_n->link_n); +} + +void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + enum transcoder transcoder = crtc->cpu_transcoder; - /* FDI link */ - pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); - lane = 0; - /* CPU eDP doesn't require FDI link, so just set DP M/N - according to current link config */ - if (is_cpu_edp) { - intel_edp_link_config(edp_encoder, &lane, &link_bw); + if (INTEL_INFO(dev)->gen >= 5) { + I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); + I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); } else { - /* FDI is a binary signal running at ~2.7GHz, encoding - * each output octet as 10 bits. The actual frequency - * is stored as a divider into a 100MHz clock, and the - * mode pixel clock is stored in units of 1KHz. - * Hence the bw of each lane in terms of the mode signal - * is: - */ - link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; + I915_WRITE(PIPE_GMCH_DATA_M(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n->gmch_n); + I915_WRITE(PIPE_DP_LINK_M(pipe), m_n->link_m); + I915_WRITE(PIPE_DP_LINK_N(pipe), m_n->link_n); } +} - /* [e]DP over FDI requires target mode clock instead of link clock. */ - if (edp_encoder) - target_clock = intel_edp_target_clock(edp_encoder, mode); - else if (is_dp) - target_clock = mode->clock; +static void ironlake_fdi_set_m_n(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_display_mode *adjusted_mode = + &intel_crtc->config.adjusted_mode; + struct intel_link_m_n m_n = {0}; + int target_clock, lane, link_bw; + + /* FDI is a binary signal running at ~2.7GHz, encoding + * each output octet as 10 bits. The actual frequency + * is stored as a divider into a 100MHz clock, and the + * mode pixel clock is stored in units of 1KHz. + * Hence the bw of each lane in terms of the mode signal + * is: + */ + link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; + + if (intel_crtc->config.pixel_target_clock) + target_clock = intel_crtc->config.pixel_target_clock; else target_clock = adjusted_mode->clock; - if (!lane) - lane = ironlake_get_lanes_required(target_clock, link_bw, - intel_crtc->bpp); + lane = ironlake_get_lanes_required(target_clock, link_bw, + intel_crtc->config.pipe_bpp); intel_crtc->fdi_lanes = lane; - if (pixel_multiplier > 1) - link_bw *= pixel_multiplier; - intel_link_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, &m_n); + if (intel_crtc->config.pixel_multiplier > 1) + link_bw *= intel_crtc->config.pixel_multiplier; + intel_link_compute_m_n(intel_crtc->config.pipe_bpp, lane, target_clock, + link_bw, &m_n); - I915_WRITE(PIPE_DATA_M1(cpu_transcoder), TU_SIZE(m_n.tu) | m_n.gmch_m); - I915_WRITE(PIPE_DATA_N1(cpu_transcoder), m_n.gmch_n); - I915_WRITE(PIPE_LINK_M1(cpu_transcoder), m_n.link_m); - I915_WRITE(PIPE_LINK_N1(cpu_transcoder), m_n.link_n); + intel_cpu_transcoder_set_m_n(intel_crtc, &m_n); } static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, - struct drm_display_mode *adjusted_mode, intel_clock_t *clock, u32 fp) { struct drm_crtc *crtc = &intel_crtc->base; @@ -5453,9 +5476,8 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *intel_encoder; uint32_t dpll; - int factor, pixel_multiplier, num_connectors = 0; + int factor, num_connectors = 0; bool is_lvds = false, is_sdvo = false, is_tv = false; - bool is_dp = false, is_cpu_edp = false; for_each_encoder_on_crtc(dev, crtc, intel_encoder) { switch (intel_encoder->type) { @@ -5471,14 +5493,6 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, case INTEL_OUTPUT_TVOUT: is_tv = true; break; - case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; - break; - case INTEL_OUTPUT_EDP: - is_dp = true; - if (!intel_encoder_is_pch_edp(&intel_encoder->base)) - is_cpu_edp = true; - break; } num_connectors++; @@ -5504,13 +5518,14 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, else dpll |= DPLLB_MODE_DAC_SERIAL; if (is_sdvo) { - pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); - if (pixel_multiplier > 1) { - dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; + if (intel_crtc->config.pixel_multiplier > 1) { + dpll |= (intel_crtc->config.pixel_multiplier - 1) + << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; } dpll |= DPLL_DVO_HIGH_SPEED; } - if (is_dp && !is_cpu_edp) + if (intel_crtc->config.has_dp_encoder && + intel_crtc->config.has_pch_encoder) dpll |= DPLL_DVO_HIGH_SPEED; /* compute bitmask from p1 value */ @@ -5548,21 +5563,22 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, } static int ironlake_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_display_mode *adjusted_mode = + &intel_crtc->config.adjusted_mode; + struct drm_display_mode *mode = &intel_crtc->config.requested_mode; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int num_connectors = 0; intel_clock_t clock, reduced_clock; u32 dpll, fp = 0, fp2 = 0; bool ok, has_reduced_clock = false; - bool is_lvds = false, is_dp = false, is_cpu_edp = false; + bool is_lvds = false; struct intel_encoder *encoder; int ret; bool dither, fdi_config_ok; @@ -5572,14 +5588,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; - break; - case INTEL_OUTPUT_EDP: - is_dp = true; - if (!intel_encoder_is_pch_edp(&encoder->base)) - is_cpu_edp = true; - break; } num_connectors++; @@ -5588,19 +5596,28 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, WARN(!(HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)), "Unexpected PCH type %d\n", INTEL_PCH_TYPE(dev)); + intel_crtc->cpu_transcoder = pipe; + ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, &has_reduced_clock, &reduced_clock); if (!ok) { DRM_ERROR("Couldn't find PLL settings for mode!\n"); return -EINVAL; } + /* Compat-code for transition, will disappear. */ + if (!intel_crtc->config.clock_set) { + intel_crtc->config.dpll.n = clock.n; + intel_crtc->config.dpll.m1 = clock.m1; + intel_crtc->config.dpll.m2 = clock.m2; + intel_crtc->config.dpll.p1 = clock.p1; + intel_crtc->config.dpll.p2 = clock.p2; + } /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); /* determine panel color depth */ - dither = intel_choose_pipe_bpp_dither(crtc, fb, &intel_crtc->bpp, - adjusted_mode); + dither = intel_crtc->config.dither; if (is_lvds && dev_priv->lvds_dither) dither = true; @@ -5609,13 +5626,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | reduced_clock.m2; - dpll = ironlake_compute_dpll(intel_crtc, adjusted_mode, &clock, fp); + dpll = ironlake_compute_dpll(intel_crtc, &clock, fp); DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ - if (!is_cpu_edp) { + if (intel_crtc->config.has_pch_encoder) { struct intel_pch_pll *pll; pll = intel_get_pch_pll(intel_crtc, dpll, fp); @@ -5627,8 +5644,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else intel_put_pch_pll(intel_crtc); - if (is_dp && !is_cpu_edp) - intel_dp_set_m_n(crtc, mode, adjusted_mode); + if (intel_crtc->config.has_dp_encoder) + intel_dp_set_m_n(intel_crtc); for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->pre_pll_enable) @@ -5663,7 +5680,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Note, this also computes intel_crtc->fdi_lanes which is used below in * ironlake_check_fdi_lanes. */ - ironlake_set_m_n(crtc, mode, adjusted_mode); + intel_crtc->fdi_lanes = 0; + if (intel_crtc->config.has_pch_encoder) + ironlake_fdi_set_m_n(crtc); fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); @@ -5684,6 +5703,23 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, return fdi_config_ok ? ret : -EINVAL; } +static bool ironlake_get_pipe_config(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = I915_READ(PIPECONF(crtc->pipe)); + if (!(tmp & PIPECONF_ENABLE)) + return false; + + if (I915_READ(TRANSCONF(crtc->pipe)) & TRANS_ENABLE) + pipe_config->has_pch_encoder = true; + + return true; +} + static void haswell_modeset_global_resources(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -5714,29 +5750,26 @@ static void haswell_modeset_global_resources(struct drm_device *dev) } static int haswell_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_display_mode *adjusted_mode = + &intel_crtc->config.adjusted_mode; + struct drm_display_mode *mode = &intel_crtc->config.requested_mode; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int num_connectors = 0; - bool is_dp = false, is_cpu_edp = false; + bool is_cpu_edp = false; struct intel_encoder *encoder; int ret; bool dither; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { - case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; - break; case INTEL_OUTPUT_EDP: - is_dp = true; if (!intel_encoder_is_pch_edp(&encoder->base)) is_cpu_edp = true; break; @@ -5745,6 +5778,11 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, num_connectors++; } + if (is_cpu_edp) + intel_crtc->cpu_transcoder = TRANSCODER_EDP; + else + intel_crtc->cpu_transcoder = pipe; + /* We are not sure yet this won't happen. */ WARN(!HAS_PCH_LPT(dev), "Unexpected PCH type %d\n", INTEL_PCH_TYPE(dev)); @@ -5764,25 +5802,24 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, intel_crtc_update_cursor(crtc, true); /* determine panel color depth */ - dither = intel_choose_pipe_bpp_dither(crtc, fb, &intel_crtc->bpp, - adjusted_mode); + dither = intel_crtc->config.dither; DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - if (is_dp && !is_cpu_edp) - intel_dp_set_m_n(crtc, mode, adjusted_mode); + if (intel_crtc->config.has_dp_encoder) + intel_dp_set_m_n(intel_crtc); intel_crtc->lowfreq_avail = false; intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); - if (!is_dp || is_cpu_edp) - ironlake_set_m_n(crtc, mode, adjusted_mode); + if (intel_crtc->config.has_pch_encoder) + ironlake_fdi_set_m_n(crtc); haswell_set_pipeconf(crtc, adjusted_mode, dither); - intel_set_pipe_csc(crtc, adjusted_mode); + intel_set_pipe_csc(crtc); /* Set up the display plane register */ I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE); @@ -5797,9 +5834,32 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, return ret; } +static bool haswell_get_pipe_config(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = I915_READ(PIPECONF(crtc->cpu_transcoder)); + if (!(tmp & PIPECONF_ENABLE)) + return false; + + /* + * aswell has only FDI/PCH transcoder A. It is which is connected to + * DDI E. So just check whether this pipe is wired to DDI E and whether + * the PCH transcoder is on. + */ + tmp = I915_READ(TRANS_DDI_FUNC_CTL(crtc->pipe)); + if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) && + I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE) + pipe_config->has_pch_encoder = true; + + + return true; +} + static int intel_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *fb) { @@ -5808,18 +5868,16 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_encoder_helper_funcs *encoder_funcs; struct intel_encoder *encoder; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_display_mode *adjusted_mode = + &intel_crtc->config.adjusted_mode; + struct drm_display_mode *mode = &intel_crtc->config.requested_mode; int pipe = intel_crtc->pipe; int ret; - if (IS_HASWELL(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) - intel_crtc->cpu_transcoder = TRANSCODER_EDP; - else - intel_crtc->cpu_transcoder = pipe; - drm_vblank_pre_modeset(dev, pipe); - ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, - x, y, fb); + ret = dev_priv->display.crtc_mode_set(crtc, x, y, fb); + drm_vblank_post_modeset(dev, pipe); if (ret != 0) @@ -5830,8 +5888,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, encoder->base.base.id, drm_get_encoder_name(&encoder->base), mode->base.id, mode->name); - encoder_funcs = encoder->base.helper_private; - encoder_funcs->mode_set(&encoder->base, mode, adjusted_mode); + if (encoder->mode_set) { + encoder->mode_set(encoder); + } else { + encoder_funcs = encoder->base.helper_private; + encoder_funcs->mode_set(&encoder->base, mode, adjusted_mode); + } } return 0; @@ -6299,13 +6361,24 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, /* we only need to pin inside GTT if cursor is non-phy */ mutex_lock(&dev->struct_mutex); if (!dev_priv->info->cursor_needs_physical) { + unsigned alignment; + if (obj->tiling_mode) { DRM_ERROR("cursor cannot be tiled\n"); ret = -EINVAL; goto fail_locked; } - ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL); + /* Note that the w/a also requires 2 PTE of padding following + * the bo. We currently fill all unused PTE with the shadow + * page and so we should always have valid PTE following the + * cursor preventing the VT-d warning. + */ + alignment = 0; + if (need_vtd_wa(dev)) + alignment = 64*1024; + + ret = i915_gem_object_pin_to_display_plane(obj, alignment, NULL); if (ret) { DRM_ERROR("failed to move cursor bo into the GTT\n"); goto fail_locked; @@ -6410,20 +6483,6 @@ static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, intel_crtc_load_lut(crtc); } -/** - * Get a pipe with a simple mode set on it for doing load-based monitor - * detection. - * - * It will be up to the load-detect code to adjust the pipe as appropriate for - * its requirements. The pipe will be connected to no other encoders. - * - * Currently this code will only succeed if there is a pipe with no encoders - * configured for it. In the future, it could choose to temporarily disable - * some outputs to free up a pipe for its use. - * - * \return crtc, or NULL if no pipes are available. - */ - /* VESA 640x480x72Hz mode to set on the pipe */ static struct drm_display_mode load_detect_mode = { DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664, @@ -6928,7 +6987,6 @@ static void do_intel_finish_page_flip(struct drm_device *dev, drm_i915_private_t *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_unpin_work *work; - struct drm_i915_gem_object *obj; unsigned long flags; /* Ignore early vblank irqs */ @@ -6958,8 +7016,6 @@ static void do_intel_finish_page_flip(struct drm_device *dev, spin_unlock_irqrestore(&dev->event_lock, flags); - obj = work->old_fb_obj; - wake_up_all(&dev_priv->pending_flip_queue); queue_work(dev_priv->wq, &work->work); @@ -7256,8 +7312,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_framebuffer *intel_fb; - struct drm_i915_gem_object *obj; + struct drm_framebuffer *old_fb = crtc->fb; + struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_unpin_work *work; unsigned long flags; @@ -7282,8 +7338,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->event = event; work->crtc = crtc; - intel_fb = to_intel_framebuffer(crtc->fb); - work->old_fb_obj = intel_fb->obj; + work->old_fb_obj = to_intel_framebuffer(old_fb)->obj; INIT_WORK(&work->work, intel_unpin_work_fn); ret = drm_vblank_get(dev, intel_crtc->pipe); @@ -7303,9 +7358,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, intel_crtc->unpin_work = work; spin_unlock_irqrestore(&dev->event_lock, flags); - intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; - if (atomic_read(&intel_crtc->unpin_work_count) >= 2) flush_workqueue(dev_priv->wq); @@ -7340,6 +7392,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, cleanup_pending: atomic_dec(&intel_crtc->unpin_work_count); + crtc->fb = old_fb; drm_gem_object_unreference(&work->old_fb_obj->base); drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); @@ -7450,19 +7503,93 @@ static void intel_modeset_commit_output_state(struct drm_device *dev) } } -static struct drm_display_mode * -intel_modeset_adjusted_mode(struct drm_crtc *crtc, - struct drm_display_mode *mode) +static int +pipe_config_set_bpp(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->dev; + struct drm_connector *connector; + int bpp; + + switch (fb->pixel_format) { + case DRM_FORMAT_C8: + bpp = 8*3; /* since we go through a colormap */ + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: + /* checked in intel_framebuffer_init already */ + if (WARN_ON(INTEL_INFO(dev)->gen > 3)) + return -EINVAL; + case DRM_FORMAT_RGB565: + bpp = 6*3; /* min is 18bpp */ + break; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + /* checked in intel_framebuffer_init already */ + if (WARN_ON(INTEL_INFO(dev)->gen < 4)) + return -EINVAL; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + bpp = 8*3; + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + /* checked in intel_framebuffer_init already */ + if (WARN_ON(INTEL_INFO(dev)->gen < 4)) + return -EINVAL; + bpp = 10*3; + break; + /* TODO: gen4+ supports 16 bpc floating point, too. */ + default: + DRM_DEBUG_KMS("unsupported depth\n"); + return -EINVAL; + } + + pipe_config->pipe_bpp = bpp; + + /* Clamp display bpp to EDID value */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + if (connector->encoder && connector->encoder->crtc != crtc) + continue; + + /* Don't use an invalid EDID bpc value */ + if (connector->display_info.bpc && + connector->display_info.bpc * 3 < bpp) { + DRM_DEBUG_KMS("clamping display bpp (was %d) to EDID reported max of %d\n", + bpp, connector->display_info.bpc*3); + pipe_config->pipe_bpp = connector->display_info.bpc*3; + } + } + + return bpp; +} + +static struct intel_crtc_config * +intel_modeset_pipe_config(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_display_mode *mode) { struct drm_device *dev = crtc->dev; - struct drm_display_mode *adjusted_mode; struct drm_encoder_helper_funcs *encoder_funcs; struct intel_encoder *encoder; + struct intel_crtc_config *pipe_config; + int plane_bpp; - adjusted_mode = drm_mode_duplicate(dev, mode); - if (!adjusted_mode) + pipe_config = kzalloc(sizeof(*pipe_config), GFP_KERNEL); + if (!pipe_config) return ERR_PTR(-ENOMEM); + drm_mode_copy(&pipe_config->adjusted_mode, mode); + drm_mode_copy(&pipe_config->requested_mode, mode); + + plane_bpp = pipe_config_set_bpp(crtc, fb, pipe_config); + if (plane_bpp < 0) + goto fail; + /* Pass our mode to the connectors and the CRTC to give them a chance to * adjust it according to limitations or connector properties, and also * a chance to reject the mode entirely. @@ -7472,23 +7599,38 @@ intel_modeset_adjusted_mode(struct drm_crtc *crtc, if (&encoder->new_crtc->base != crtc) continue; + + if (encoder->compute_config) { + if (!(encoder->compute_config(encoder, pipe_config))) { + DRM_DEBUG_KMS("Encoder config failure\n"); + goto fail; + } + + continue; + } + encoder_funcs = encoder->base.helper_private; - if (!(encoder_funcs->mode_fixup(&encoder->base, mode, - adjusted_mode))) { + if (!(encoder_funcs->mode_fixup(&encoder->base, + &pipe_config->requested_mode, + &pipe_config->adjusted_mode))) { DRM_DEBUG_KMS("Encoder fixup failed\n"); goto fail; } } - if (!(intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) { + if (!(intel_crtc_compute_config(crtc, pipe_config))) { DRM_DEBUG_KMS("CRTC fixup failed\n"); goto fail; } DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); - return adjusted_mode; + pipe_config->dither = pipe_config->pipe_bpp != plane_bpp; + DRM_DEBUG_KMS("plane bpp: %i, pipe bpp: %i, dithering: %i\n", + plane_bpp, pipe_config->pipe_bpp, pipe_config->dither); + + return pipe_config; fail: - drm_mode_destroy(dev, adjusted_mode); + kfree(pipe_config); return ERR_PTR(-EINVAL); } @@ -7650,12 +7792,29 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) base.head) \ if (mask & (1 <<(intel_crtc)->pipe)) \ +static bool +intel_pipe_config_compare(struct intel_crtc_config *current_config, + struct intel_crtc_config *pipe_config) +{ + if (current_config->has_pch_encoder != pipe_config->has_pch_encoder) { + DRM_ERROR("mismatch in has_pch_encoder " + "(expected %i, found %i)\n", + current_config->has_pch_encoder, + pipe_config->has_pch_encoder); + return false; + } + + return true; +} + void intel_modeset_check_state(struct drm_device *dev) { + drm_i915_private_t *dev_priv = dev->dev_private; struct intel_crtc *crtc; struct intel_encoder *encoder; struct intel_connector *connector; + struct intel_crtc_config pipe_config; list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) { @@ -7744,7 +7903,16 @@ intel_modeset_check_state(struct drm_device *dev) "crtc's computed enabled state doesn't match tracked enabled state " "(expected %i, found %i)\n", enabled, crtc->base.enabled); - assert_pipe(dev->dev_private, crtc->pipe, crtc->active); + memset(&pipe_config, 0, sizeof(pipe_config)); + active = dev_priv->display.get_pipe_config(crtc, + &pipe_config); + WARN(crtc->active != active, + "crtc active state doesn't match with hw state " + "(expected %i, found %i)\n", crtc->active, active); + + WARN(active && + !intel_pipe_config_compare(&crtc->config, &pipe_config), + "pipe state doesn't match!\n"); } } @@ -7754,7 +7922,8 @@ int intel_set_mode(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode; + struct drm_display_mode *saved_mode, *saved_hwmode; + struct intel_crtc_config *pipe_config = NULL; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; int ret = 0; @@ -7767,12 +7936,6 @@ int intel_set_mode(struct drm_crtc *crtc, intel_modeset_affected_pipes(crtc, &modeset_pipes, &prepare_pipes, &disable_pipes); - DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", - modeset_pipes, prepare_pipes, disable_pipes); - - for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) - intel_crtc_disable(&intel_crtc->base); - *saved_hwmode = crtc->hwmode; *saved_mode = crtc->mode; @@ -7781,15 +7944,22 @@ int intel_set_mode(struct drm_crtc *crtc, * Hence simply check whether any bit is set in modeset_pipes in all the * pieces of code that are not yet converted to deal with mutliple crtcs * changing their mode at the same time. */ - adjusted_mode = NULL; if (modeset_pipes) { - adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); - if (IS_ERR(adjusted_mode)) { - ret = PTR_ERR(adjusted_mode); + pipe_config = intel_modeset_pipe_config(crtc, fb, mode); + if (IS_ERR(pipe_config)) { + ret = PTR_ERR(pipe_config); + pipe_config = NULL; + goto out; } } + DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", + modeset_pipes, prepare_pipes, disable_pipes); + + for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) + intel_crtc_disable(&intel_crtc->base); + for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) { if (intel_crtc->base.enabled) dev_priv->display.crtc_disable(&intel_crtc->base); @@ -7798,8 +7968,12 @@ int intel_set_mode(struct drm_crtc *crtc, /* crtc->mode is already used by the ->mode_set callbacks, hence we need * to set it here already despite that we pass it down the callchain. */ - if (modeset_pipes) + if (modeset_pipes) { crtc->mode = *mode; + /* mode_set/enable/disable functions rely on a correct pipe + * config. */ + to_intel_crtc(crtc)->config = *pipe_config; + } /* Only after disabling all output pipelines that will be changed can we * update the the output configuration. */ @@ -7813,7 +7987,6 @@ int intel_set_mode(struct drm_crtc *crtc, */ for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { ret = intel_crtc_mode_set(&intel_crtc->base, - mode, adjusted_mode, x, y, fb); if (ret) goto done; @@ -7825,7 +7998,7 @@ int intel_set_mode(struct drm_crtc *crtc, if (modeset_pipes) { /* Store real post-adjustment hardware mode. */ - crtc->hwmode = *adjusted_mode; + crtc->hwmode = pipe_config->adjusted_mode; /* Calculate and store various constants which * are later needed by vblank and swap-completion @@ -7836,7 +8009,6 @@ int intel_set_mode(struct drm_crtc *crtc, /* FIXME: add subpixel order */ done: - drm_mode_destroy(dev, adjusted_mode); if (ret && crtc->enabled) { crtc->hwmode = *saved_hwmode; crtc->mode = *saved_mode; @@ -7845,6 +8017,7 @@ done: } out: + kfree(pipe_config); kfree(saved_mode); return ret; } @@ -7936,10 +8109,8 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set, config->mode_changed = true; } else if (set->fb == NULL) { config->mode_changed = true; - } else if (set->fb->depth != set->crtc->fb->depth) { - config->mode_changed = true; - } else if (set->fb->bits_per_pixel != - set->crtc->fb->bits_per_pixel) { + } else if (set->fb->pixel_format != + set->crtc->fb->pixel_format) { config->mode_changed = true; } else config->fb_changed = true; @@ -8122,6 +8293,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) goto fail; } } else if (config->fb_changed) { + intel_crtc_wait_for_pending_flips(set->crtc); + ret = intel_pipe_set_base(set->crtc, set->x, set->y, set->fb); } @@ -8209,8 +8382,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base; dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; - intel_crtc->bpp = 24; /* default for pre-Ironlake */ - drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); } @@ -8320,20 +8491,20 @@ static void intel_setup_outputs(struct drm_device *dev) if (has_edp_a(dev)) intel_dp_init(dev, DP_A, PORT_A); - if (I915_READ(HDMIB) & PORT_DETECTED) { + if (I915_READ(PCH_HDMIB) & SDVO_DETECTED) { /* PCH SDVOB multiplex with HDMIB */ found = intel_sdvo_init(dev, PCH_SDVOB, true); if (!found) - intel_hdmi_init(dev, HDMIB, PORT_B); + intel_hdmi_init(dev, PCH_HDMIB, PORT_B); if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED)) intel_dp_init(dev, PCH_DP_B, PORT_B); } - if (I915_READ(HDMIC) & PORT_DETECTED) - intel_hdmi_init(dev, HDMIC, PORT_C); + if (I915_READ(PCH_HDMIC) & SDVO_DETECTED) + intel_hdmi_init(dev, PCH_HDMIC, PORT_C); - if (!dpd_is_edp && I915_READ(HDMID) & PORT_DETECTED) - intel_hdmi_init(dev, HDMID, PORT_D); + if (!dpd_is_edp && I915_READ(PCH_HDMID) & SDVO_DETECTED) + intel_hdmi_init(dev, PCH_HDMID, PORT_D); if (I915_READ(PCH_DP_C) & DP_DETECTED) intel_dp_init(dev, PCH_DP_C, PORT_C); @@ -8345,24 +8516,21 @@ static void intel_setup_outputs(struct drm_device *dev) if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED) intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C); - if (I915_READ(VLV_DISPLAY_BASE + SDVOB) & PORT_DETECTED) { - intel_hdmi_init(dev, VLV_DISPLAY_BASE + SDVOB, PORT_B); + if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) { + intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB, + PORT_B); if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED) intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B); } - - if (I915_READ(VLV_DISPLAY_BASE + SDVOC) & PORT_DETECTED) - intel_hdmi_init(dev, VLV_DISPLAY_BASE + SDVOC, PORT_C); - } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) { bool found = false; - if (I915_READ(SDVOB) & SDVO_DETECTED) { + if (I915_READ(GEN3_SDVOB) & SDVO_DETECTED) { DRM_DEBUG_KMS("probing SDVOB\n"); - found = intel_sdvo_init(dev, SDVOB, true); + found = intel_sdvo_init(dev, GEN3_SDVOB, true); if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) { DRM_DEBUG_KMS("probing HDMI on SDVOB\n"); - intel_hdmi_init(dev, SDVOB, PORT_B); + intel_hdmi_init(dev, GEN4_HDMIB, PORT_B); } if (!found && SUPPORTS_INTEGRATED_DP(dev)) { @@ -8373,16 +8541,16 @@ static void intel_setup_outputs(struct drm_device *dev) /* Before G4X SDVOC doesn't have its own detect register */ - if (I915_READ(SDVOB) & SDVO_DETECTED) { + if (I915_READ(GEN3_SDVOB) & SDVO_DETECTED) { DRM_DEBUG_KMS("probing SDVOC\n"); - found = intel_sdvo_init(dev, SDVOC, false); + found = intel_sdvo_init(dev, GEN3_SDVOC, false); } - if (!found && (I915_READ(SDVOC) & SDVO_DETECTED)) { + if (!found && (I915_READ(GEN3_SDVOC) & SDVO_DETECTED)) { if (SUPPORTS_INTEGRATED_HDMI(dev)) { DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); - intel_hdmi_init(dev, SDVOC, PORT_C); + intel_hdmi_init(dev, GEN4_HDMIC, PORT_C); } if (SUPPORTS_INTEGRATED_DP(dev)) { DRM_DEBUG_KMS("probing DP_C\n"); @@ -8549,20 +8717,22 @@ static void intel_init_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - /* We always want a DPMS function */ if (HAS_DDI(dev)) { + dev_priv->display.get_pipe_config = haswell_get_pipe_config; dev_priv->display.crtc_mode_set = haswell_crtc_mode_set; dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; dev_priv->display.off = haswell_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; } else if (HAS_PCH_SPLIT(dev)) { + dev_priv->display.get_pipe_config = ironlake_get_pipe_config; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; dev_priv->display.crtc_enable = ironlake_crtc_enable; dev_priv->display.crtc_disable = ironlake_crtc_disable; dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; } else { + dev_priv->display.get_pipe_config = i9xx_get_pipe_config; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_enable = i9xx_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; @@ -8805,7 +8975,7 @@ void intel_modeset_init_hw(struct drm_device *dev) void intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int i, ret; + int i, j, ret; drm_mode_config_init(dev); @@ -8836,13 +9006,17 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.fb_base = dev_priv->gtt.mappable_base; DRM_DEBUG_KMS("%d display pipe%s available.\n", - dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); + INTEL_INFO(dev)->num_pipes, + INTEL_INFO(dev)->num_pipes > 1 ? "s" : ""); - for (i = 0; i < dev_priv->num_pipe; i++) { + for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { intel_crtc_init(dev, i); - ret = intel_plane_init(dev, i); - if (ret) - DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret); + for (j = 0; j < dev_priv->num_plane; j++) { + ret = intel_plane_init(dev, i, j); + if (ret) + DRM_DEBUG_KMS("pipe %d plane %d init failed: %d\n", + i, j, ret); + } } intel_cpu_pll_init(dev); @@ -8895,10 +9069,11 @@ static void intel_enable_pipe_a(struct drm_device *dev) static bool intel_check_plane_mapping(struct intel_crtc *crtc) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; u32 reg, val; - if (dev_priv->num_pipe == 1) + if (INTEL_INFO(dev)->num_pipes == 1) return true; reg = DSPCNTR(!crtc->plane); @@ -9054,6 +9229,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; u32 tmp; + struct drm_plane *plane; struct intel_crtc *crtc; struct intel_encoder *encoder; struct intel_connector *connector; @@ -9073,6 +9249,13 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, case TRANS_DDI_EDP_INPUT_C_ONOFF: pipe = PIPE_C; break; + default: + /* A bogus value has been programmed, disable + * the transcoder */ + WARN(1, "Bogus eDP source %08x\n", tmp); + intel_ddi_disable_transcoder_func(dev_priv, + TRANSCODER_EDP); + goto setup_pipes; } crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); @@ -9083,14 +9266,12 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, } } - for_each_pipe(pipe) { - crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - - tmp = I915_READ(PIPECONF(crtc->cpu_transcoder)); - if (tmp & PIPECONF_ENABLE) - crtc->active = true; - else - crtc->active = false; +setup_pipes: + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + base.head) { + memset(&crtc->config, 0, sizeof(crtc->config)); + crtc->active = dev_priv->display.get_pipe_config(crtc, + &crtc->config); crtc->base.enabled = crtc->active; @@ -9150,8 +9331,12 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, if (force_restore) { for_each_pipe(pipe) { - intel_crtc_restore_mode(dev_priv->pipe_to_crtc_mapping[pipe]); + struct drm_crtc *crtc = + dev_priv->pipe_to_crtc_mapping[pipe]; + intel_crtc_restore_mode(crtc); } + list_for_each_entry(plane, &dev->mode_config.plane_list, head) + intel_plane_restore(plane); i915_redisable_vga(dev); } else { @@ -9300,15 +9485,24 @@ intel_display_capture_error_state(struct drm_device *dev) for_each_pipe(i) { cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i); - error->cursor[i].control = I915_READ(CURCNTR(i)); - error->cursor[i].position = I915_READ(CURPOS(i)); - error->cursor[i].base = I915_READ(CURBASE(i)); + if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) { + error->cursor[i].control = I915_READ(CURCNTR(i)); + error->cursor[i].position = I915_READ(CURPOS(i)); + error->cursor[i].base = I915_READ(CURBASE(i)); + } else { + error->cursor[i].control = I915_READ(CURCNTR_IVB(i)); + error->cursor[i].position = I915_READ(CURPOS_IVB(i)); + error->cursor[i].base = I915_READ(CURBASE_IVB(i)); + } error->plane[i].control = I915_READ(DSPCNTR(i)); error->plane[i].stride = I915_READ(DSPSTRIDE(i)); - error->plane[i].size = I915_READ(DSPSIZE(i)); - error->plane[i].pos = I915_READ(DSPPOS(i)); - error->plane[i].addr = I915_READ(DSPADDR(i)); + if (INTEL_INFO(dev)->gen <= 3) { + error->plane[i].size = I915_READ(DSPSIZE(i)); + error->plane[i].pos = I915_READ(DSPPOS(i)); + } + if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) + error->plane[i].addr = I915_READ(DSPADDR(i)); if (INTEL_INFO(dev)->gen >= 4) { error->plane[i].surface = I915_READ(DSPSURF(i)); error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i)); @@ -9332,10 +9526,9 @@ intel_display_print_error_state(struct seq_file *m, struct drm_device *dev, struct intel_display_error_state *error) { - drm_i915_private_t *dev_priv = dev->dev_private; int i; - seq_printf(m, "Num Pipes: %d\n", dev_priv->num_pipe); + seq_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); for_each_pipe(i) { seq_printf(m, "Pipe [%d]:\n", i); seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); @@ -9350,9 +9543,12 @@ intel_display_print_error_state(struct seq_file *m, seq_printf(m, "Plane [%d]:\n", i); seq_printf(m, " CNTR: %08x\n", error->plane[i].control); seq_printf(m, " STRIDE: %08x\n", error->plane[i].stride); - seq_printf(m, " SIZE: %08x\n", error->plane[i].size); - seq_printf(m, " POS: %08x\n", error->plane[i].pos); - seq_printf(m, " ADDR: %08x\n", error->plane[i].addr); + if (INTEL_INFO(dev)->gen <= 3) { + seq_printf(m, " SIZE: %08x\n", error->plane[i].size); + seq_printf(m, " POS: %08x\n", error->plane[i].pos); + } + if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) + seq_printf(m, " ADDR: %08x\n", error->plane[i].addr); if (INTEL_INFO(dev)->gen >= 4) { seq_printf(m, " SURF: %08x\n", error->plane[i].surface); seq_printf(m, " TILEOFF: %08x\n", error->plane[i].tile_offset); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f61cb79..b30e82b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -109,29 +109,6 @@ bool intel_encoder_is_pch_edp(struct drm_encoder *encoder) static void intel_dp_link_down(struct intel_dp *intel_dp); -void -intel_edp_link_config(struct intel_encoder *intel_encoder, - int *lane_num, int *link_bw) -{ - struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); - - *lane_num = intel_dp->lane_count; - *link_bw = drm_dp_bw_code_to_link_rate(intel_dp->link_bw); -} - -int -intel_edp_target_clock(struct intel_encoder *intel_encoder, - struct drm_display_mode *mode) -{ - struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); - struct intel_connector *intel_connector = intel_dp->attached_connector; - - if (intel_connector->panel.fixed_mode) - return intel_connector->panel.fixed_mode->clock; - else - return mode->clock; -} - static int intel_dp_max_link_bw(struct intel_dp *intel_dp) { @@ -177,34 +154,6 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes) return (max_link_clock * max_lanes * 8) / 10; } -static bool -intel_dp_adjust_dithering(struct intel_dp *intel_dp, - struct drm_display_mode *mode, - bool adjust_mode) -{ - int max_link_clock = - drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); - int max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); - int max_rate, mode_rate; - - mode_rate = intel_dp_link_required(mode->clock, 24); - max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); - - if (mode_rate > max_rate) { - mode_rate = intel_dp_link_required(mode->clock, 18); - if (mode_rate > max_rate) - return false; - - if (adjust_mode) - mode->private_flags - |= INTEL_MODE_DP_FORCE_6BPC; - - return true; - } - - return true; -} - static int intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -212,6 +161,8 @@ intel_dp_mode_valid(struct drm_connector *connector, struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_connector *intel_connector = to_intel_connector(connector); struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + int target_clock = mode->clock; + int max_rate, mode_rate, max_lanes, max_link_clock; if (is_edp(intel_dp) && fixed_mode) { if (mode->hdisplay > fixed_mode->hdisplay) @@ -219,9 +170,17 @@ intel_dp_mode_valid(struct drm_connector *connector, if (mode->vdisplay > fixed_mode->vdisplay) return MODE_PANEL; + + target_clock = fixed_mode->clock; } - if (!intel_dp_adjust_dithering(intel_dp, mode, false)) + max_link_clock = drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); + max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); + + max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); + mode_rate = intel_dp_link_required(target_clock, 18); + + if (mode_rate > max_rate) return MODE_CLOCK_HIGH; if (mode->clock < 10000) @@ -294,16 +253,20 @@ static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_stat_reg; - return (I915_READ(PCH_PP_STATUS) & PP_ON) != 0; + pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; + return (I915_READ(pp_stat_reg) & PP_ON) != 0; } static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_ctrl_reg; - return (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0; + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + return (I915_READ(pp_ctrl_reg) & EDP_FORCE_VDD) != 0; } static void @@ -311,14 +274,19 @@ intel_dp_check_edp(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_stat_reg, pp_ctrl_reg; if (!is_edp(intel_dp)) return; + + pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) { WARN(1, "eDP powered off while attempting aux channel communication.\n"); DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", - I915_READ(PCH_PP_STATUS), - I915_READ(PCH_PP_CONTROL)); + I915_READ(pp_stat_reg), + I915_READ(pp_ctrl_reg)); } } @@ -328,32 +296,14 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t ch_ctl = intel_dp->output_reg + 0x10; + uint32_t ch_ctl = intel_dp->aux_ch_ctl_reg; uint32_t status; bool done; - if (IS_HASWELL(dev)) { - switch (intel_dig_port->port) { - case PORT_A: - ch_ctl = DPA_AUX_CH_CTL; - break; - case PORT_B: - ch_ctl = PCH_DPB_AUX_CH_CTL; - break; - case PORT_C: - ch_ctl = PCH_DPC_AUX_CH_CTL; - break; - case PORT_D: - ch_ctl = PCH_DPD_AUX_CH_CTL; - break; - default: - BUG(); - } - } - #define C (((status = I915_READ_NOTRACE(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) if (has_aux_irq) - done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, + msecs_to_jiffies(10)); else done = wait_for_atomic(C, 10) == 0; if (!done) @@ -369,11 +319,10 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, uint8_t *recv, int recv_size) { - uint32_t output_reg = intel_dp->output_reg; struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t ch_ctl = output_reg + 0x10; + uint32_t ch_ctl = intel_dp->aux_ch_ctl_reg; uint32_t ch_data = ch_ctl + 4; int i, ret, recv_bytes; uint32_t status; @@ -387,29 +336,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, */ pm_qos_update_request(&dev_priv->pm_qos, 0); - if (IS_HASWELL(dev)) { - switch (intel_dig_port->port) { - case PORT_A: - ch_ctl = DPA_AUX_CH_CTL; - ch_data = DPA_AUX_CH_DATA1; - break; - case PORT_B: - ch_ctl = PCH_DPB_AUX_CH_CTL; - ch_data = PCH_DPB_AUX_CH_DATA1; - break; - case PORT_C: - ch_ctl = PCH_DPC_AUX_CH_CTL; - ch_data = PCH_DPC_AUX_CH_DATA1; - break; - case PORT_D: - ch_ctl = PCH_DPD_AUX_CH_CTL; - ch_data = PCH_DPD_AUX_CH_DATA1; - break; - default: - BUG(); - } - } - intel_dp_check_edp(intel_dp); /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the @@ -731,18 +657,26 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, } bool -intel_dp_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +intel_dp_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { - struct drm_device *dev = encoder->dev; - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + struct drm_display_mode *mode = &pipe_config->requested_mode; + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct intel_connector *intel_connector = intel_dp->attached_connector; int lane_count, clock; int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; int bpp, mode_rate; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; + int target_clock, link_avail, link_clock; + + if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev) && !is_cpu_edp(intel_dp)) + pipe_config->has_pch_encoder = true; + + pipe_config->has_dp_encoder = true; if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { intel_fixed_panel_mode(intel_connector->panel.fixed_mode, @@ -751,6 +685,8 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, intel_connector->panel.fitting_mode, mode, adjusted_mode); } + /* We need to take the panel's fixed mode into account. */ + target_clock = adjusted_mode->clock; if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) return false; @@ -759,11 +695,28 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, "max bw %02x pixel clock %iKHz\n", max_lane_count, bws[max_clock], adjusted_mode->clock); - if (!intel_dp_adjust_dithering(intel_dp, adjusted_mode, true)) - return false; + /* Walk through all bpp values. Luckily they're all nicely spaced with 2 + * bpc in between. */ + bpp = min_t(int, 8*3, pipe_config->pipe_bpp); + for (; bpp >= 6*3; bpp -= 2*3) { + mode_rate = intel_dp_link_required(target_clock, bpp); + + for (clock = 0; clock <= max_clock; clock++) { + for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { + link_clock = drm_dp_bw_code_to_link_rate(bws[clock]); + link_avail = intel_dp_max_data_rate(link_clock, + lane_count); + + if (mode_rate <= link_avail) { + goto found; + } + } + } + } - bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + return false; +found: if (intel_dp->color_range_auto) { /* * See: @@ -777,94 +730,38 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, } if (intel_dp->color_range) - adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; - - mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp); - - for (clock = 0; clock <= max_clock; clock++) { - for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - int link_bw_clock = - drm_dp_bw_code_to_link_rate(bws[clock]); - int link_avail = intel_dp_max_data_rate(link_bw_clock, - lane_count); - - if (mode_rate <= link_avail) { - intel_dp->link_bw = bws[clock]; - intel_dp->lane_count = lane_count; - adjusted_mode->clock = link_bw_clock; - DRM_DEBUG_KMS("DP link bw %02x lane " - "count %d clock %d bpp %d\n", - intel_dp->link_bw, intel_dp->lane_count, - adjusted_mode->clock, bpp); - DRM_DEBUG_KMS("DP link bw required %i available %i\n", - mode_rate, link_avail); - return true; - } - } - } + pipe_config->limited_color_range = true; - return false; -} + intel_dp->link_bw = bws[clock]; + intel_dp->lane_count = lane_count; + adjusted_mode->clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw); + pipe_config->pixel_target_clock = target_clock; -void -intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = crtc->dev; - struct intel_encoder *intel_encoder; - struct intel_dp *intel_dp; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int lane_count = 4; - struct intel_link_m_n m_n; - int pipe = intel_crtc->pipe; - enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; + DRM_DEBUG_KMS("DP link bw %02x lane count %d clock %d bpp %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock, bpp); + DRM_DEBUG_KMS("DP link bw required %i available %i\n", + mode_rate, link_avail); - /* - * Find the lane count in the intel_encoder private - */ - for_each_encoder_on_crtc(dev, crtc, intel_encoder) { - intel_dp = enc_to_intel_dp(&intel_encoder->base); - - if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT || - intel_encoder->type == INTEL_OUTPUT_EDP) - { - lane_count = intel_dp->lane_count; - break; - } - } + intel_link_compute_m_n(bpp, lane_count, + target_clock, adjusted_mode->clock, + &pipe_config->dp_m_n); /* - * Compute the GMCH and Link ratios. The '3' here is - * the number of bytes_per_pixel post-LUT, which we always - * set up for 8-bits of R/G/B, or 3 bytes total. + * XXX: We have a strange regression where using the vbt edp bpp value + * for the link bw computation results in black screens, the panel only + * works when we do the computation at the usual 24bpp (but still + * requires us to use 18bpp). Until that's fully debugged, stay + * bug-for-bug compatible with the old code. */ - intel_link_compute_m_n(intel_crtc->bpp, lane_count, - mode->clock, adjusted_mode->clock, &m_n); - - if (IS_HASWELL(dev)) { - I915_WRITE(PIPE_DATA_M1(cpu_transcoder), - TU_SIZE(m_n.tu) | m_n.gmch_m); - I915_WRITE(PIPE_DATA_N1(cpu_transcoder), m_n.gmch_n); - I915_WRITE(PIPE_LINK_M1(cpu_transcoder), m_n.link_m); - I915_WRITE(PIPE_LINK_N1(cpu_transcoder), m_n.link_n); - } else if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m); - I915_WRITE(TRANSDATA_N1(pipe), m_n.gmch_n); - I915_WRITE(TRANSDPLINK_M1(pipe), m_n.link_m); - I915_WRITE(TRANSDPLINK_N1(pipe), m_n.link_n); - } else if (IS_VALLEYVIEW(dev)) { - I915_WRITE(PIPE_DATA_M1(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m); - I915_WRITE(PIPE_DATA_N1(pipe), m_n.gmch_n); - I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m); - I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n); - } else { - I915_WRITE(PIPE_GMCH_DATA_M(pipe), - TU_SIZE(m_n.tu) | m_n.gmch_m); - I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n); - I915_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m); - I915_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n); + if (is_edp(intel_dp) && dev_priv->edp.bpp) { + DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", + bpp, dev_priv->edp.bpp); + bpp = min_t(int, bpp, dev_priv->edp.bpp); } + pipe_config->pipe_bpp = bpp; + + return true; } void intel_dp_init_link_config(struct intel_dp *intel_dp) @@ -983,7 +880,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, else intel_dp->DP |= DP_PLL_FREQ_270MHZ; } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { - if (!HAS_PCH_SPLIT(dev)) + if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev)) intel_dp->DP |= intel_dp->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) @@ -998,7 +895,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (intel_crtc->pipe == 1) intel_dp->DP |= DP_PIPEB_SELECT; - if (is_cpu_edp(intel_dp)) { + if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { /* don't miss out required setting for eDP */ if (adjusted_mode->clock < 200000) intel_dp->DP |= DP_PLL_FREQ_160MHZ; @@ -1009,7 +906,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; } - if (is_cpu_edp(intel_dp)) + if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) ironlake_set_pll_edp(crtc, adjusted_mode->clock); } @@ -1028,16 +925,20 @@ static void ironlake_wait_panel_status(struct intel_dp *intel_dp, { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_stat_reg, pp_ctrl_reg; + + pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; DRM_DEBUG_KMS("mask %08x value %08x status %08x control %08x\n", - mask, value, - I915_READ(PCH_PP_STATUS), - I915_READ(PCH_PP_CONTROL)); + mask, value, + I915_READ(pp_stat_reg), + I915_READ(pp_ctrl_reg)); - if (_wait_for((I915_READ(PCH_PP_STATUS) & mask) == value, 5000, 10)) { + if (_wait_for((I915_READ(pp_stat_reg) & mask) == value, 5000, 10)) { DRM_ERROR("Panel status timeout: status %08x control %08x\n", - I915_READ(PCH_PP_STATUS), - I915_READ(PCH_PP_CONTROL)); + I915_READ(pp_stat_reg), + I915_READ(pp_ctrl_reg)); } } @@ -1064,9 +965,15 @@ static void ironlake_wait_panel_power_cycle(struct intel_dp *intel_dp) * is locked */ -static u32 ironlake_get_pp_control(struct drm_i915_private *dev_priv) +static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) { - u32 control = I915_READ(PCH_PP_CONTROL); + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + u32 control; + u32 pp_ctrl_reg; + + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + control = I915_READ(pp_ctrl_reg); control &= ~PANEL_UNLOCK_MASK; control |= PANEL_UNLOCK_REGS; @@ -1078,6 +985,7 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + u32 pp_stat_reg, pp_ctrl_reg; if (!is_edp(intel_dp)) return; @@ -1096,13 +1004,16 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) if (!ironlake_edp_have_panel_power(intel_dp)) ironlake_wait_panel_power_cycle(intel_dp); - pp = ironlake_get_pp_control(dev_priv); + pp = ironlake_get_pp_control(intel_dp); pp |= EDP_FORCE_VDD; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); - DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", - I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); + pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); + DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", + I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); /* * If the panel wasn't on, delay before accessing aux channel */ @@ -1117,19 +1028,23 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + u32 pp_stat_reg, pp_ctrl_reg; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { - pp = ironlake_get_pp_control(dev_priv); + pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_FORCE_VDD; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); - /* Make sure sequencer is idle before allowing subsequent activity */ - DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", - I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); + pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); + /* Make sure sequencer is idle before allowing subsequent activity */ + DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", + I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); msleep(intel_dp->panel_power_down_delay); } } @@ -1173,6 +1088,7 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + u32 pp_ctrl_reg; if (!is_edp(intel_dp)) return; @@ -1186,7 +1102,7 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp) ironlake_wait_panel_power_cycle(intel_dp); - pp = ironlake_get_pp_control(dev_priv); + pp = ironlake_get_pp_control(intel_dp); if (IS_GEN5(dev)) { /* ILK workaround: disable reset around power sequence */ pp &= ~PANEL_POWER_RESET; @@ -1198,8 +1114,10 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp) if (!IS_GEN5(dev)) pp |= PANEL_POWER_RESET; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); ironlake_wait_panel_on(intel_dp); @@ -1215,6 +1133,7 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + u32 pp_ctrl_reg; if (!is_edp(intel_dp)) return; @@ -1223,12 +1142,15 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp) WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); - pp = ironlake_get_pp_control(dev_priv); + pp = ironlake_get_pp_control(intel_dp); /* We need to switch off panel power _and_ force vdd, for otherwise some * panels get very unhappy and cease to work. */ pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE); - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); intel_dp->want_panel_vdd = false; @@ -1242,6 +1164,7 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; int pipe = to_intel_crtc(intel_dig_port->base.base.crtc)->pipe; u32 pp; + u32 pp_ctrl_reg; if (!is_edp(intel_dp)) return; @@ -1254,10 +1177,13 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp) * allowing it to appear. */ msleep(intel_dp->backlight_on_delay); - pp = ironlake_get_pp_control(dev_priv); + pp = ironlake_get_pp_control(intel_dp); pp |= EDP_BLC_ENABLE; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); intel_panel_enable_backlight(dev, pipe); } @@ -1267,6 +1193,7 @@ void ironlake_edp_backlight_off(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + u32 pp_ctrl_reg; if (!is_edp(intel_dp)) return; @@ -1274,10 +1201,13 @@ void ironlake_edp_backlight_off(struct intel_dp *intel_dp) intel_panel_disable_backlight(dev); DRM_DEBUG_KMS("\n"); - pp = ironlake_get_pp_control(dev_priv); + pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_BLC_ENABLE; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); msleep(intel_dp->backlight_off_delay); } @@ -1373,7 +1303,7 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, if (!(tmp & DP_PORT_EN)) return false; - if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) { + if (is_cpu_edp(intel_dp) && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) { *pipe = PORT_TO_PIPE_CPT(tmp); } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { *pipe = PORT_TO_PIPE(tmp); @@ -1430,10 +1360,12 @@ static void intel_disable_dp(struct intel_encoder *encoder) static void intel_post_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct drm_device *dev = encoder->base.dev; if (is_cpu_edp(intel_dp)) { intel_dp_link_down(intel_dp); - ironlake_edp_pll_off(intel_dp); + if (!IS_VALLEYVIEW(dev)) + ironlake_edp_pll_off(intel_dp); } } @@ -1459,8 +1391,9 @@ static void intel_enable_dp(struct intel_encoder *encoder) static void intel_pre_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct drm_device *dev = encoder->base.dev; - if (is_cpu_edp(intel_dp)) + if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) ironlake_edp_pll_on(intel_dp); } @@ -1537,7 +1470,7 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) { struct drm_device *dev = intel_dp_to_dev(intel_dp); - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: return DP_TRAIN_PRE_EMPHASIS_9_5; @@ -1745,7 +1678,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) uint32_t signal_levels, mask; uint8_t train_set = intel_dp->train_set[0]; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { signal_levels = intel_hsw_signal_levels(train_set); mask = DDI_BUF_EMP_MASK; } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { @@ -1776,7 +1709,7 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, int ret; uint32_t temp; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { temp = I915_READ(DP_TP_CTL(port)); if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE) @@ -1929,7 +1862,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; - if (i == intel_dp->lane_count && voltage_tries == 5) { + if (i == intel_dp->lane_count) { ++loop_tries; if (loop_tries == 5) { DRM_DEBUG_KMS("too many full retries, give up\n"); @@ -2300,6 +2233,16 @@ g4x_dp_detect(struct intel_dp *intel_dp) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); uint32_t bit; + /* Can't disconnect eDP, but you can close the lid... */ + if (is_edp(intel_dp)) { + enum drm_connector_status status; + + status = intel_panel_detect(dev); + if (status == connector_status_unknown) + status = connector_status_connected; + return status; + } + switch (intel_dig_port->port) { case PORT_B: bit = PORTB_HOTPLUG_LIVE_STATUS; @@ -2548,18 +2491,20 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) { struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = intel_dp_to_dev(intel_dp); i2c_del_adapter(&intel_dp->adapter); drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); + mutex_lock(&dev->mode_config.mutex); ironlake_panel_vdd_off_sync(intel_dp); + mutex_unlock(&dev->mode_config.mutex); } kfree(intel_dig_port); } static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { - .mode_fixup = intel_dp_mode_fixup, .mode_set = intel_dp_mode_set, }; @@ -2655,15 +2600,28 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct edp_power_seq cur, vbt, spec, final; u32 pp_on, pp_off, pp_div, pp; + int pp_control_reg, pp_on_reg, pp_off_reg, pp_div_reg; + + if (HAS_PCH_SPLIT(dev)) { + pp_control_reg = PCH_PP_CONTROL; + pp_on_reg = PCH_PP_ON_DELAYS; + pp_off_reg = PCH_PP_OFF_DELAYS; + pp_div_reg = PCH_PP_DIVISOR; + } else { + pp_control_reg = PIPEA_PP_CONTROL; + pp_on_reg = PIPEA_PP_ON_DELAYS; + pp_off_reg = PIPEA_PP_OFF_DELAYS; + pp_div_reg = PIPEA_PP_DIVISOR; + } /* Workaround: Need to write PP_CONTROL with the unlock key as * the very first thing. */ - pp = ironlake_get_pp_control(dev_priv); - I915_WRITE(PCH_PP_CONTROL, pp); + pp = ironlake_get_pp_control(intel_dp); + I915_WRITE(pp_control_reg, pp); - pp_on = I915_READ(PCH_PP_ON_DELAYS); - pp_off = I915_READ(PCH_PP_OFF_DELAYS); - pp_div = I915_READ(PCH_PP_DIVISOR); + pp_on = I915_READ(pp_on_reg); + pp_off = I915_READ(pp_off_reg); + pp_div = I915_READ(pp_div_reg); /* Pull timing values out of registers */ cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> @@ -2738,7 +2696,22 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, struct edp_power_seq *seq) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_on, pp_off, pp_div; + u32 pp_on, pp_off, pp_div, port_sel = 0; + int div = HAS_PCH_SPLIT(dev) ? intel_pch_rawclk(dev) : intel_hrawclk(dev); + int pp_on_reg, pp_off_reg, pp_div_reg; + + if (HAS_PCH_SPLIT(dev)) { + pp_on_reg = PCH_PP_ON_DELAYS; + pp_off_reg = PCH_PP_OFF_DELAYS; + pp_div_reg = PCH_PP_DIVISOR; + } else { + pp_on_reg = PIPEA_PP_ON_DELAYS; + pp_off_reg = PIPEA_PP_OFF_DELAYS; + pp_div_reg = PIPEA_PP_DIVISOR; + } + + if (IS_VALLEYVIEW(dev)) + port_sel = I915_READ(pp_on_reg) & 0xc0000000; /* And finally store the new values in the power sequencer. */ pp_on = (seq->t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) | @@ -2747,8 +2720,7 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, (seq->t10 << PANEL_POWER_DOWN_DELAY_SHIFT); /* Compute the divisor for the pp clock, simply match the Bspec * formula. */ - pp_div = ((100 * intel_pch_rawclk(dev))/2 - 1) - << PP_REFERENCE_DIVIDER_SHIFT; + pp_div = ((100 * div)/2 - 1) << PP_REFERENCE_DIVIDER_SHIFT; pp_div |= (DIV_ROUND_UP(seq->t11_t12, 1000) << PANEL_POWER_CYCLE_DELAY_SHIFT); @@ -2756,19 +2728,21 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, * power sequencer any more. */ if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { if (is_cpu_edp(intel_dp)) - pp_on |= PANEL_POWER_PORT_DP_A; + port_sel = PANEL_POWER_PORT_DP_A; else - pp_on |= PANEL_POWER_PORT_DP_D; + port_sel = PANEL_POWER_PORT_DP_D; } - I915_WRITE(PCH_PP_ON_DELAYS, pp_on); - I915_WRITE(PCH_PP_OFF_DELAYS, pp_off); - I915_WRITE(PCH_PP_DIVISOR, pp_div); + pp_on |= port_sel; + + I915_WRITE(pp_on_reg, pp_on); + I915_WRITE(pp_off_reg, pp_off); + I915_WRITE(pp_div_reg, pp_div); DRM_DEBUG_KMS("panel power sequencer register settings: PP_ON %#x, PP_OFF %#x, PP_DIV %#x\n", - I915_READ(PCH_PP_ON_DELAYS), - I915_READ(PCH_PP_OFF_DELAYS), - I915_READ(PCH_PP_DIVISOR)); + I915_READ(pp_on_reg), + I915_READ(pp_off_reg), + I915_READ(pp_div_reg)); } void @@ -2830,27 +2804,46 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, else intel_connector->get_hw_state = intel_connector_get_hw_state; + intel_dp->aux_ch_ctl_reg = intel_dp->output_reg + 0x10; + if (HAS_DDI(dev)) { + switch (intel_dig_port->port) { + case PORT_A: + intel_dp->aux_ch_ctl_reg = DPA_AUX_CH_CTL; + break; + case PORT_B: + intel_dp->aux_ch_ctl_reg = PCH_DPB_AUX_CH_CTL; + break; + case PORT_C: + intel_dp->aux_ch_ctl_reg = PCH_DPC_AUX_CH_CTL; + break; + case PORT_D: + intel_dp->aux_ch_ctl_reg = PCH_DPD_AUX_CH_CTL; + break; + default: + BUG(); + } + } /* Set up the DDC bus. */ switch (port) { case PORT_A: + intel_encoder->hpd_pin = HPD_PORT_A; name = "DPDDC-A"; break; case PORT_B: - dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; + intel_encoder->hpd_pin = HPD_PORT_B; name = "DPDDC-B"; break; case PORT_C: - dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; + intel_encoder->hpd_pin = HPD_PORT_C; name = "DPDDC-C"; break; case PORT_D: - dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; + intel_encoder->hpd_pin = HPD_PORT_D; name = "DPDDC-D"; break; default: - WARN(1, "Invalid port %c\n", port_name(port)); - break; + BUG(); } if (is_edp(intel_dp)) @@ -2960,6 +2953,7 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&intel_encoder->base, &intel_dp_helper_funcs); + intel_encoder->compute_config = intel_dp_compute_config; intel_encoder->enable = intel_enable_dp; intel_encoder->pre_enable = intel_pre_enable_dp; intel_encoder->disable = intel_disable_dp; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 07ebac6..d7bd031 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -33,12 +33,21 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_dp_helper.h> +/** + * _wait_for - magic (register) wait macro + * + * Does the right thing for modeset paths when run under kdgb or similar atomic + * contexts. Note that it's important that we check the condition again after + * 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, MS, W) ({ \ - unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \ int ret__ = 0; \ while (!(COND)) { \ if (time_after(jiffies, timeout__)) { \ - ret__ = -ETIMEDOUT; \ + if (!(COND)) \ + ret__ = -ETIMEDOUT; \ break; \ } \ if (W && drm_can_sleep()) { \ @@ -50,21 +59,10 @@ ret__; \ }) -#define wait_for_atomic_us(COND, US) ({ \ - unsigned long timeout__ = jiffies + usecs_to_jiffies(US); \ - int ret__ = 0; \ - while (!(COND)) { \ - if (time_after(jiffies, timeout__)) { \ - ret__ = -ETIMEDOUT; \ - break; \ - } \ - cpu_relax(); \ - } \ - ret__; \ -}) - #define wait_for(COND, MS) _wait_for(COND, MS, 1) #define wait_for_atomic(COND, MS) _wait_for(COND, MS, 0) +#define wait_for_atomic_us(COND, US) _wait_for((COND), \ + DIV_ROUND_UP((US), 1000), 0) #define KHz(x) (1000*x) #define MHz(x) KHz(1000*x) @@ -101,34 +99,6 @@ #define INTEL_DVO_CHIP_TMDS 2 #define INTEL_DVO_CHIP_TVOUT 4 -/* drm_display_mode->private_flags */ -#define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0) -#define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT) -#define INTEL_MODE_DP_FORCE_6BPC (0x10) -/* This flag must be set by the encoder's mode_fixup if it changes the crtc - * timings in the mode to prevent the crtc fixup from overwriting them. - * Currently only lvds needs that. */ -#define INTEL_MODE_CRTC_TIMINGS_SET (0x20) -/* - * Set when limited 16-235 (as opposed to full 0-255) RGB color range is - * to be used. - */ -#define INTEL_MODE_LIMITED_COLOR_RANGE (0x40) - -static inline void -intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, - int multiplier) -{ - mode->clock *= multiplier; - mode->private_flags |= multiplier; -} - -static inline int -intel_mode_get_pixel_multiplier(const struct drm_display_mode *mode) -{ - return (mode->private_flags & INTEL_MODE_PIXEL_MULTIPLIER_MASK) >> INTEL_MODE_PIXEL_MULTIPLIER_SHIFT; -} - struct intel_framebuffer { struct drm_framebuffer base; struct drm_i915_gem_object *obj; @@ -158,9 +128,12 @@ struct intel_encoder { bool cloneable; bool connectors_active; void (*hot_plug)(struct intel_encoder *); + bool (*compute_config)(struct intel_encoder *, + struct intel_crtc_config *); void (*pre_pll_enable)(struct intel_encoder *); void (*pre_enable)(struct intel_encoder *); void (*enable)(struct intel_encoder *); + void (*mode_set)(struct intel_encoder *intel_encoder); void (*disable)(struct intel_encoder *); void (*post_disable)(struct intel_encoder *); /* Read out the current hw state of this connector, returning true if @@ -168,6 +141,7 @@ struct intel_encoder { * it is connected to in the pipe parameter. */ bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe); int crtc_mask; + enum hpd_pin hpd_pin; }; struct intel_panel { @@ -199,6 +173,51 @@ struct intel_connector { struct edid *edid; }; +struct intel_crtc_config { + struct drm_display_mode requested_mode; + struct drm_display_mode adjusted_mode; + /* This flag must be set by the encoder's compute_config callback if it + * changes the crtc timings in the mode to prevent the crtc fixup from + * overwriting them. Currently only lvds needs that. */ + bool timings_set; + /* Whether to set up the PCH/FDI. Note that we never allow sharing + * between pch encoders and cpu encoders. */ + bool has_pch_encoder; + + /* + * Use reduced/limited/broadcast rbg range, compressing from the full + * range fed into the crtcs. + */ + bool limited_color_range; + + /* DP has a bunch of special case unfortunately, so mark the pipe + * accordingly. */ + bool has_dp_encoder; + bool dither; + + /* Controls for the clock computation, to override various stages. */ + bool clock_set; + + /* Settings for the intel dpll used on pretty much everything but + * haswell. */ + struct dpll { + unsigned n; + unsigned m1, m2; + unsigned p1, p2; + } dpll; + + int pipe_bpp; + struct intel_link_m_n dp_m_n; + /** + * This is currently used by DP and HDMI encoders since those can have a + * target pixel clock != the port link clock (which is currently stored + * in adjusted_mode->clock). + */ + int pixel_target_clock; + /* Used by SDVO (and if we ever fix it, HDMI). */ + unsigned pixel_multiplier; +}; + struct intel_crtc { struct drm_crtc base; enum pipe pipe; @@ -230,7 +249,8 @@ struct intel_crtc { int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; bool cursor_visible; - unsigned int bpp; + + struct intel_crtc_config config; /* We can share PLLs across outputs if the timings match */ struct intel_pch_pll *pch_pll; @@ -242,11 +262,16 @@ struct intel_crtc { struct intel_plane { struct drm_plane base; + int plane; enum pipe pipe; struct drm_i915_gem_object *obj; bool can_scale; int max_downscale; u32 lut_r[1024], lut_g[1024], lut_b[1024]; + int crtc_x, crtc_y; + unsigned int crtc_w, crtc_h; + uint32_t src_x, src_y; + uint32_t src_w, src_h; void (*update_plane)(struct drm_plane *plane, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj, @@ -347,7 +372,7 @@ struct dip_infoframe { } __attribute__((packed)); struct intel_hdmi { - u32 sdvox_reg; + u32 hdmi_reg; int ddc_bus; uint32_t color_range; bool color_range_auto; @@ -366,6 +391,7 @@ struct intel_hdmi { struct intel_dp { uint32_t output_reg; + uint32_t aux_ch_ctl_reg; uint32_t DP; uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; bool has_audio; @@ -443,13 +469,12 @@ extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector) extern void intel_crt_init(struct drm_device *dev); extern void intel_hdmi_init(struct drm_device *dev, - int sdvox_reg, enum port port); + int hdmi_reg, enum port port); extern void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); -extern bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); +extern bool intel_hdmi_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config); extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); @@ -464,18 +489,14 @@ extern void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); -void -intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); extern void intel_dp_init_link_config(struct intel_dp *intel_dp); extern void intel_dp_start_link_train(struct intel_dp *intel_dp); extern void intel_dp_complete_link_train(struct intel_dp *intel_dp); extern void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode); extern void intel_dp_encoder_destroy(struct drm_encoder *encoder); extern void intel_dp_check_link_status(struct intel_dp *intel_dp); -extern bool intel_dp_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); +extern bool intel_dp_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config); extern bool intel_dpd_is_edp(struct drm_device *dev); extern void ironlake_edp_backlight_on(struct intel_dp *intel_dp); extern void ironlake_edp_backlight_off(struct intel_dp *intel_dp); @@ -483,11 +504,8 @@ extern void ironlake_edp_panel_on(struct intel_dp *intel_dp); extern void ironlake_edp_panel_off(struct intel_dp *intel_dp); extern void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); extern void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); -extern void intel_edp_link_config(struct intel_encoder *, int *, int *); -extern int intel_edp_target_clock(struct intel_encoder *, - struct drm_display_mode *mode); extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder); -extern int intel_plane_init(struct drm_device *dev, enum pipe pipe); +extern int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane); extern void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane); @@ -531,6 +549,7 @@ extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); extern void intel_connector_dpms(struct drm_connector *, int mode); extern bool intel_connector_get_hw_state(struct intel_connector *connector); extern void intel_modeset_check_state(struct drm_device *dev); +extern void intel_plane_restore(struct drm_plane *plane); static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) @@ -636,6 +655,10 @@ extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); +extern void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n); +extern void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n); extern void intel_prepare_ddi(struct drm_device *dev); extern void hsw_fdi_link_train(struct drm_crtc *crtc); extern void intel_ddi_init(struct drm_device *dev, enum port port); @@ -681,7 +704,7 @@ extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe); extern int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv); extern void intel_ddi_pll_init(struct drm_device *dev); -extern void intel_ddi_enable_pipe_func(struct drm_crtc *crtc); +extern void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc); extern void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder); extern void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc); @@ -695,4 +718,6 @@ extern bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); extern void intel_ddi_fdi_disable(struct drm_crtc *crtc); +extern void intel_display_handle_reset(struct drm_device *dev); + #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 981bdce..8d81c929 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -150,7 +150,8 @@ static int intelfb_create(struct drm_fb_helper *helper, } info->screen_size = size; -// memset(info->screen_base, 0, size); + /* This driver doesn't need a VT switch to restore the mode on resume */ + info->skip_vt_switch = true; drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); @@ -227,7 +228,7 @@ int intel_fbdev_init(struct drm_device *dev) ifbdev->helper.funcs = &intel_fb_helper_funcs; ret = drm_fb_helper_init(dev, &ifbdev->helper, - dev_priv->num_pipe, + INTEL_INFO(dev)->num_pipes, INTELFB_CONN_LIMIT); if (ret) { kfree(ifbdev); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index fa8ec4a..ee4a8da 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -50,7 +50,7 @@ assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi) enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; - WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits, + WARN(I915_READ(intel_hdmi->hdmi_reg) & enabled_bits, "HDMI port enabled, expecting disabled\n"); } @@ -120,13 +120,14 @@ static u32 hsw_infoframe_enable(struct dip_infoframe *frame) } } -static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, enum pipe pipe) +static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, + enum transcoder cpu_transcoder) { switch (frame->type) { case DIP_TYPE_AVI: - return HSW_TVIDEO_DIP_AVI_DATA(pipe); + return HSW_TVIDEO_DIP_AVI_DATA(cpu_transcoder); case DIP_TYPE_SPD: - return HSW_TVIDEO_DIP_SPD_DATA(pipe); + return HSW_TVIDEO_DIP_SPD_DATA(cpu_transcoder); default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); return 0; @@ -293,8 +294,8 @@ static void hsw_write_infoframe(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); - u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe); - u32 data_reg = hsw_infoframe_data_reg(frame, intel_crtc->pipe); + u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->cpu_transcoder); + u32 data_reg = hsw_infoframe_data_reg(frame, intel_crtc->cpu_transcoder); unsigned int i, len = DIP_HEADER_SIZE + frame->len; u32 val = I915_READ(ctl_reg); @@ -332,6 +333,7 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, .ver = DIP_VERSION_AVI, @@ -342,7 +344,7 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; if (intel_hdmi->rgb_quant_range_selectable) { - if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + if (intel_crtc->config.limited_color_range) avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; else avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; @@ -568,7 +570,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder, struct drm_i915_private *dev_priv = encoder->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe); + u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->cpu_transcoder); u32 val = I915_READ(reg); assert_hdmi_port_disabled(intel_hdmi); @@ -597,40 +599,40 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - u32 sdvox; + u32 hdmi_val; - sdvox = SDVO_ENCODING_HDMI; - if (!HAS_PCH_SPLIT(dev)) - sdvox |= intel_hdmi->color_range; + hdmi_val = SDVO_ENCODING_HDMI; + if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev)) + hdmi_val |= intel_hdmi->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) - sdvox |= SDVO_VSYNC_ACTIVE_HIGH; + hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) - sdvox |= SDVO_HSYNC_ACTIVE_HIGH; + hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH; - if (intel_crtc->bpp > 24) - sdvox |= COLOR_FORMAT_12bpc; + if (intel_crtc->config.pipe_bpp > 24) + hdmi_val |= HDMI_COLOR_FORMAT_12bpc; else - sdvox |= COLOR_FORMAT_8bpc; + hdmi_val |= SDVO_COLOR_FORMAT_8bpc; /* Required on CPT */ if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev)) - sdvox |= HDMI_MODE_SELECT; + hdmi_val |= HDMI_MODE_SELECT_HDMI; if (intel_hdmi->has_audio) { DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n", pipe_name(intel_crtc->pipe)); - sdvox |= SDVO_AUDIO_ENABLE; - sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC; + hdmi_val |= SDVO_AUDIO_ENABLE; + hdmi_val |= HDMI_MODE_SELECT_HDMI; intel_write_eld(encoder, adjusted_mode); } if (HAS_PCH_CPT(dev)) - sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe); - else if (intel_crtc->pipe == PIPE_B) - sdvox |= SDVO_PIPE_B_SELECT; + hdmi_val |= SDVO_PIPE_SEL_CPT(intel_crtc->pipe); + else + hdmi_val |= SDVO_PIPE_SEL(intel_crtc->pipe); - I915_WRITE(intel_hdmi->sdvox_reg, sdvox); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val); + POSTING_READ(intel_hdmi->hdmi_reg); intel_hdmi->set_infoframes(encoder, adjusted_mode); } @@ -643,7 +645,7 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); u32 tmp; - tmp = I915_READ(intel_hdmi->sdvox_reg); + tmp = I915_READ(intel_hdmi->hdmi_reg); if (!(tmp & SDVO_ENABLE)) return false; @@ -660,6 +662,7 @@ static void intel_enable_hdmi(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); u32 temp; u32 enable_bits = SDVO_ENABLE; @@ -667,38 +670,32 @@ static void intel_enable_hdmi(struct intel_encoder *encoder) if (intel_hdmi->has_audio) enable_bits |= SDVO_AUDIO_ENABLE; - temp = I915_READ(intel_hdmi->sdvox_reg); + temp = I915_READ(intel_hdmi->hdmi_reg); /* HW workaround for IBX, we need to move the port to transcoder A - * before disabling it. */ - if (HAS_PCH_IBX(dev)) { - struct drm_crtc *crtc = encoder->base.crtc; - int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; - - /* Restore the transcoder select bit. */ - if (pipe == PIPE_B) - enable_bits |= SDVO_PIPE_B_SELECT; - } + * before disabling it, so restore the transcoder select bit here. */ + if (HAS_PCH_IBX(dev)) + enable_bits |= SDVO_PIPE_SEL(intel_crtc->pipe); /* HW workaround, need to toggle enable bit off and on for 12bpc, but * we do this anyway which shows more stable in testing. */ if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); + POSTING_READ(intel_hdmi->hdmi_reg); } temp |= enable_bits; - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); /* HW workaround, need to write this twice for issue that may result * in first write getting masked. */ if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); } } @@ -710,7 +707,7 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) u32 temp; u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE; - temp = I915_READ(intel_hdmi->sdvox_reg); + temp = I915_READ(intel_hdmi->hdmi_reg); /* HW workaround for IBX, we need to move the port to transcoder A * before disabling it. */ @@ -720,12 +717,12 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) if (temp & SDVO_PIPE_B_SELECT) { temp &= ~SDVO_PIPE_B_SELECT; - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); /* Again we need to write this twice. */ - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); /* Transcoder selection bits only update * effectively on vblank. */ @@ -740,21 +737,21 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) * we do this anyway which shows more stable in testing. */ if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); + POSTING_READ(intel_hdmi->hdmi_reg); } temp &= ~enable_bits; - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); /* HW workaround, need to write this twice for issue that may result * in first write getting masked. */ if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); } } @@ -772,23 +769,40 @@ static int intel_hdmi_mode_valid(struct drm_connector *connector, return MODE_OK; } -bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +bool intel_hdmi_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; if (intel_hdmi->color_range_auto) { /* See CEA-861-E - 5.1 Default Encoding Parameters */ if (intel_hdmi->has_hdmi_sink && drm_match_cea_mode(adjusted_mode) > 1) - intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235; else intel_hdmi->color_range = 0; } if (intel_hdmi->color_range) - adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + pipe_config->limited_color_range = true; + + if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev)) + pipe_config->has_pch_encoder = true; + + /* + * HDMI is either 12 or 8, so if the display lets 10bpc sneak + * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi + * outputs. + */ + if (pipe_config->pipe_bpp > 8*3 && HAS_PCH_SPLIT(dev)) { + DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n"); + pipe_config->pipe_bpp = 12*3; + } else { + DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n"); + pipe_config->pipe_bpp = 8*3; + } return true; } @@ -916,7 +930,7 @@ intel_hdmi_set_property(struct drm_connector *connector, break; case INTEL_BROADCAST_RGB_LIMITED: intel_hdmi->color_range_auto = false; - intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235; break; default: return -EINVAL; @@ -941,7 +955,6 @@ static void intel_hdmi_destroy(struct drm_connector *connector) } static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { - .mode_fixup = intel_hdmi_mode_fixup, .mode_set = intel_hdmi_mode_set, }; @@ -992,29 +1005,30 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, switch (port) { case PORT_B: intel_hdmi->ddc_bus = GMBUS_PORT_DPB; - dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; + intel_encoder->hpd_pin = HPD_PORT_B; break; case PORT_C: intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; + intel_encoder->hpd_pin = HPD_PORT_C; break; case PORT_D: intel_hdmi->ddc_bus = GMBUS_PORT_DPD; - dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; + intel_encoder->hpd_pin = HPD_PORT_D; break; case PORT_A: + intel_encoder->hpd_pin = HPD_PORT_A; /* Internal port only for eDP. */ default: BUG(); } - if (!HAS_PCH_SPLIT(dev)) { - intel_hdmi->write_infoframe = g4x_write_infoframe; - intel_hdmi->set_infoframes = g4x_set_infoframes; - } else if (IS_VALLEYVIEW(dev)) { + if (IS_VALLEYVIEW(dev)) { intel_hdmi->write_infoframe = vlv_write_infoframe; intel_hdmi->set_infoframes = vlv_set_infoframes; - } else if (IS_HASWELL(dev)) { + } else if (!HAS_PCH_SPLIT(dev)) { + intel_hdmi->write_infoframe = g4x_write_infoframe; + intel_hdmi->set_infoframes = g4x_set_infoframes; + } else if (HAS_DDI(dev)) { intel_hdmi->write_infoframe = hsw_write_infoframe; intel_hdmi->set_infoframes = hsw_set_infoframes; } else if (HAS_PCH_IBX(dev)) { @@ -1045,7 +1059,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, } } -void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) +void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) { struct intel_digital_port *intel_dig_port; struct intel_encoder *intel_encoder; @@ -1069,6 +1083,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); + intel_encoder->compute_config = intel_hdmi_compute_config; intel_encoder->enable = intel_enable_hdmi; intel_encoder->disable = intel_disable_hdmi; intel_encoder->get_hw_state = intel_hdmi_get_hw_state; @@ -1078,7 +1093,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) intel_encoder->cloneable = false; intel_dig_port->port = port; - intel_dig_port->hdmi.sdvox_reg = sdvox_reg; + intel_dig_port->hdmi.hdmi_reg = hdmi_reg; intel_dig_port->dp.output_reg = 0; intel_hdmi_init_connector(intel_dig_port, intel_connector); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index acf8aec..ef4744e 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -203,7 +203,13 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) algo->data = bus; } -#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4) +/* + * gmbus on gen4 seems to be able to generate legacy interrupts even when in MSI + * mode. This results in spurious interrupt warnings if the legacy irq no. is + * shared with another device. The kernel then disables that interrupt source + * and so prevents the other device from working properly. + */ +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5) static int gmbus_wait_hw_status(struct drm_i915_private *dev_priv, u32 gmbus2_status, @@ -214,6 +220,9 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv, u32 gmbus2 = 0; DEFINE_WAIT(wait); + if (!HAS_GMBUS_IRQ(dev_priv->dev)) + gmbus4_irq_en = 0; + /* Important: The hw handles only the first bit, so set only one! Since * we also need to check for NAKs besides the hw ready/idle signal, we * need to wake up periodically and check that ourselves. */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 3d1d974..ca2d903 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -261,8 +261,6 @@ centre_horizontally(struct drm_display_mode *mode, mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; - - mode->private_flags |= INTEL_MODE_CRTC_TIMINGS_SET; } static void @@ -284,8 +282,6 @@ centre_vertically(struct drm_display_mode *mode, mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; - - mode->private_flags |= INTEL_MODE_CRTC_TIMINGS_SET; } static inline u32 panel_fitter_scaling(u32 source, u32 target) @@ -301,17 +297,20 @@ static inline u32 panel_fitter_scaling(u32 source, u32 target) return (FACTOR * ratio + FACTOR/2) / FACTOR; } -static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, + struct intel_crtc_config *pipe_config) { - struct drm_device *dev = encoder->dev; + struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder); + struct intel_lvds_encoder *lvds_encoder = + to_lvds_encoder(&intel_encoder->base); struct intel_connector *intel_connector = &lvds_encoder->attached_connector->base; + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; + unsigned int lvds_bpp; int pipe; /* Should never happen!! */ @@ -323,6 +322,17 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, if (intel_encoder_check_is_cloned(&lvds_encoder->base)) return false; + if ((I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK) == + LVDS_A3_POWER_UP) + lvds_bpp = 8*3; + else + lvds_bpp = 6*3; + + if (lvds_bpp != pipe_config->pipe_bpp) { + DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", + pipe_config->pipe_bpp, lvds_bpp); + pipe_config->pipe_bpp = lvds_bpp; + } /* * We have timings from the BIOS for the panel, put them in * to the adjusted mode. The CRTC will be set up for this mode, @@ -333,6 +343,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, adjusted_mode); if (HAS_PCH_SPLIT(dev)) { + pipe_config->has_pch_encoder = true; + intel_pch_panel_fitting(dev, intel_connector->panel.fitting_mode, mode, adjusted_mode); @@ -359,6 +371,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, I915_WRITE(BCLRPAT(pipe), 0); drm_mode_set_crtcinfo(adjusted_mode, 0); + pipe_config->timings_set = true; switch (intel_connector->panel.fitting_mode) { case DRM_MODE_SCALE_CENTER: @@ -661,7 +674,6 @@ static int intel_lvds_set_property(struct drm_connector *connector, } static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { - .mode_fixup = intel_lvds_mode_fixup, .mode_set = intel_lvds_mode_set, }; @@ -850,6 +862,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Fujitsu Esprimo Q900", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"), + }, + }, { } /* terminating entry */ }; @@ -1019,12 +1039,15 @@ static bool intel_lvds_supported(struct drm_device *dev) { /* With the introduction of the PCH we gained a dedicated * LVDS presence pin, use it. */ - if (HAS_PCH_SPLIT(dev)) + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) return true; /* Otherwise LVDS was only attached to mobile products, * except for the inglorious 830gm */ - return IS_MOBILE(dev) && !IS_I830(dev); + if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev)) + return true; + + return false; } /** @@ -1102,6 +1125,7 @@ bool intel_lvds_init(struct drm_device *dev) intel_encoder->enable = intel_enable_lvds; intel_encoder->pre_enable = intel_pre_enable_lvds; intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; + intel_encoder->compute_config = intel_lvds_compute_config; intel_encoder->disable = intel_disable_lvds; intel_encoder->get_hw_state = intel_lvds_get_hw_state; intel_connector->get_hw_state = intel_connector_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index a3730e0..7874cec 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -286,8 +286,11 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level) { struct drm_i915_private *dev_priv = dev->dev_private; - dev_priv->backlight_level = level; - if (dev_priv->backlight_enabled) + dev_priv->backlight.level = level; + if (dev_priv->backlight.device) + dev_priv->backlight.device->props.brightness = level; + + if (dev_priv->backlight.enabled) intel_panel_actually_set_backlight(dev, level); } @@ -295,7 +298,7 @@ void intel_panel_disable_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - dev_priv->backlight_enabled = false; + dev_priv->backlight.enabled = false; intel_panel_actually_set_backlight(dev, 0); if (INTEL_INFO(dev)->gen >= 4) { @@ -318,11 +321,12 @@ void intel_panel_enable_backlight(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->backlight_level == 0) - dev_priv->backlight_level = intel_panel_get_max_backlight(dev); - - dev_priv->backlight_enabled = true; - intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (dev_priv->backlight.level == 0) { + dev_priv->backlight.level = intel_panel_get_max_backlight(dev); + if (dev_priv->backlight.device) + dev_priv->backlight.device->props.brightness = + dev_priv->backlight.level; + } if (INTEL_INFO(dev)->gen >= 4) { uint32_t reg, tmp; @@ -338,7 +342,7 @@ void intel_panel_enable_backlight(struct drm_device *dev, if (tmp & BLM_PWM_ENABLE) goto set_level; - if (dev_priv->num_pipe == 3) + if (INTEL_INFO(dev)->num_pipes == 3) tmp &= ~BLM_PIPE_SELECT_IVB; else tmp &= ~BLM_PIPE_SELECT; @@ -359,20 +363,20 @@ void intel_panel_enable_backlight(struct drm_device *dev, } set_level: - /* Check the current backlight level and try to set again if it's zero. - * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically - * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written. + /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. + * BLC_PWM_CPU_CTL may be cleared to zero automatically when these + * registers are set. */ - if (!intel_panel_get_backlight(dev)) - intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + dev_priv->backlight.enabled = true; + intel_panel_actually_set_backlight(dev, dev_priv->backlight.level); } static void intel_panel_init_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - dev_priv->backlight_level = intel_panel_get_backlight(dev); - dev_priv->backlight_enabled = dev_priv->backlight_level != 0; + dev_priv->backlight.level = intel_panel_get_backlight(dev); + dev_priv->backlight.enabled = dev_priv->backlight.level != 0; } enum drm_connector_status @@ -408,8 +412,7 @@ static int intel_panel_update_status(struct backlight_device *bd) static int intel_panel_get_brightness(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); - struct drm_i915_private *dev_priv = dev->dev_private; - return dev_priv->backlight_level; + return intel_panel_get_backlight(dev); } static const struct backlight_ops intel_panel_bl_ops = { @@ -427,31 +430,31 @@ int intel_panel_setup_backlight(struct drm_connector *connector) memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; + props.brightness = dev_priv->backlight.level; props.max_brightness = _intel_panel_get_max_backlight(dev); if (props.max_brightness == 0) { DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n"); return -ENODEV; } - dev_priv->backlight = + dev_priv->backlight.device = backlight_device_register("intel_backlight", &connector->kdev, dev, &intel_panel_bl_ops, &props); - if (IS_ERR(dev_priv->backlight)) { + if (IS_ERR(dev_priv->backlight.device)) { DRM_ERROR("Failed to register backlight: %ld\n", - PTR_ERR(dev_priv->backlight)); - dev_priv->backlight = NULL; + PTR_ERR(dev_priv->backlight.device)); + dev_priv->backlight.device = NULL; return -ENODEV; } - dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev); return 0; } void intel_panel_destroy_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->backlight) - backlight_device_unregister(dev_priv->backlight); + if (dev_priv->backlight.device) + backlight_device_unregister(dev_priv->backlight.device); } #else int intel_panel_setup_backlight(struct drm_connector *connector) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 61fee7f..13a0666 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2460,10 +2460,14 @@ void gen6_set_rps(struct drm_device *dev, u8 val) if (val == dev_priv->rps.cur_delay) return; - I915_WRITE(GEN6_RPNSWREQ, - GEN6_FREQUENCY(val) | - GEN6_OFFSET(0) | - GEN6_AGGRESSIVE_TURBO); + if (IS_HASWELL(dev)) + I915_WRITE(GEN6_RPNSWREQ, + HSW_FREQUENCY(val)); + else + I915_WRITE(GEN6_RPNSWREQ, + GEN6_FREQUENCY(val) | + GEN6_OFFSET(0) | + GEN6_AGGRESSIVE_TURBO); /* Make sure we continue to get interrupts * until we hit the minimum or maximum frequencies. @@ -2574,7 +2578,7 @@ static void gen6_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RC_SLEEP, 0); I915_WRITE(GEN6_RC1e_THRESHOLD, 1000); I915_WRITE(GEN6_RC6_THRESHOLD, 50000); - I915_WRITE(GEN6_RC6p_THRESHOLD, 100000); + I915_WRITE(GEN6_RC6p_THRESHOLD, 150000); I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ /* Check if we are enabling RC6 */ @@ -2601,12 +2605,19 @@ static void gen6_enable_rps(struct drm_device *dev) GEN6_RC_CTL_EI_MODE(1) | GEN6_RC_CTL_HW_ENABLE); - I915_WRITE(GEN6_RPNSWREQ, - GEN6_FREQUENCY(10) | - GEN6_OFFSET(0) | - GEN6_AGGRESSIVE_TURBO); - I915_WRITE(GEN6_RC_VIDEO_FREQ, - GEN6_FREQUENCY(12)); + if (IS_HASWELL(dev)) { + I915_WRITE(GEN6_RPNSWREQ, + HSW_FREQUENCY(10)); + I915_WRITE(GEN6_RC_VIDEO_FREQ, + HSW_FREQUENCY(12)); + } else { + I915_WRITE(GEN6_RPNSWREQ, + GEN6_FREQUENCY(10) | + GEN6_OFFSET(0) | + GEN6_AGGRESSIVE_TURBO); + I915_WRITE(GEN6_RC_VIDEO_FREQ, + GEN6_FREQUENCY(12)); + } I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000); I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, @@ -2628,12 +2639,14 @@ static void gen6_enable_rps(struct drm_device *dev) (IS_HASWELL(dev) ? GEN7_RP_DOWN_IDLE_AVG : GEN6_RP_DOWN_IDLE_CONT)); ret = sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_MIN_FREQ_TABLE, 0); - if (!ret) { + if (!ret && (IS_GEN6(dev) || IS_IVYBRIDGE(dev))) { pcu_mbox = 0; ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox); - if (ret && pcu_mbox & (1<<31)) { /* OC supported */ + if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */ + DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max from %dMHz to %dMHz\n", + (dev_priv->rps.max_delay & 0xff) * 50, + (pcu_mbox & 0xff) * 50); dev_priv->rps.max_delay = pcu_mbox & 0xff; - DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50); } } else { DRM_DEBUG_DRIVER("Failed to set the min frequency\n"); @@ -2821,7 +2834,7 @@ static void ironlake_enable_rc6(struct drm_device *dev) ret = intel_ring_idle(ring); dev_priv->mm.interruptible = was_interruptible; if (ret) { - DRM_ERROR("failed to enable ironlake power power savings\n"); + DRM_ERROR("failed to enable ironlake power savings\n"); ironlake_teardown_rc6(dev); return; } @@ -3768,6 +3781,9 @@ static void haswell_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); + /* WaSwitchSolVfFArbitrationPriority */ + I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); + /* XXX: This is a workaround for early silicon revisions and should be * removed later. */ @@ -3899,8 +3915,10 @@ static void valleyview_init_clock_gating(struct drm_device *dev) CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); + /* WaDisablePSDDualDispatchEnable */ I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, - _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); + _MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP | + GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, @@ -3968,24 +3986,20 @@ static void valleyview_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); /* - * On ValleyView, the GUnit needs to signal the GT - * when flip and other events complete. So enable - * all the GUnit->GT interrupts here - */ - I915_WRITE(VLV_DPFLIPSTAT, PIPEB_LINE_COMPARE_INT_EN | - PIPEB_HLINE_INT_EN | PIPEB_VBLANK_INT_EN | - SPRITED_FLIPDONE_INT_EN | SPRITEC_FLIPDONE_INT_EN | - PLANEB_FLIPDONE_INT_EN | PIPEA_LINE_COMPARE_INT_EN | - PIPEA_HLINE_INT_EN | PIPEA_VBLANK_INT_EN | - SPRITEB_FLIPDONE_INT_EN | SPRITEA_FLIPDONE_INT_EN | - PLANEA_FLIPDONE_INT_EN); - - /* * WaDisableVLVClockGating_VBIIssue * Disable clock gating on th GCFG unit to prevent a delay * in the reporting of vblank events. */ - I915_WRITE(VLV_GUNIT_CLOCK_GATE, GCFG_DIS); + I915_WRITE(VLV_GUNIT_CLOCK_GATE, 0xffffffff); + + /* Conservative clock gating settings for now */ + I915_WRITE(0x9400, 0xffffffff); + I915_WRITE(0x9404, 0xffffffff); + I915_WRITE(0x9408, 0xffffffff); + I915_WRITE(0x940c, 0xffffffff); + I915_WRITE(0x9410, 0xffffffff); + I915_WRITE(0x9414, 0xffffffff); + I915_WRITE(0x9418, 0xffffffff); } static void g4x_init_clock_gating(struct drm_device *dev) @@ -4076,7 +4090,10 @@ void intel_set_power_well(struct drm_device *dev, bool enable) bool is_enabled, enable_requested; uint32_t tmp; - if (!IS_HASWELL(dev)) + if (!HAS_POWER_WELL(dev)) + return; + + if (!i915_disable_power_well && !enable) return; tmp = I915_READ(HSW_PWR_WELL_DRIVER); @@ -4111,7 +4128,7 @@ void intel_init_power_well(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (!IS_HASWELL(dev)) + if (!HAS_POWER_WELL(dev)) return; /* For now, we need the power well to be always enabled. */ @@ -4271,21 +4288,14 @@ static void __gen6_gt_force_wake_reset(struct drm_i915_private *dev_priv) static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { - u32 forcewake_ack; - - if (IS_HASWELL(dev_priv->dev)) - forcewake_ack = FORCEWAKE_ACK_HSW; - else - forcewake_ack = FORCEWAKE_ACK; - - if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK) & 1) == 0, FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); - I915_WRITE_NOTRACE(FORCEWAKE, FORCEWAKE_KERNEL); + I915_WRITE_NOTRACE(FORCEWAKE, 1); POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ - if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK) & 1), FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); @@ -4308,7 +4318,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) else forcewake_ack = FORCEWAKE_MT_ACK; - if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, + if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & FORCEWAKE_KERNEL) == 0, FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); @@ -4316,7 +4326,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) /* something from same cacheline, but !FORCEWAKE_MT */ POSTING_READ(ECOBUS); - if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), + if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & FORCEWAKE_KERNEL), FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); @@ -4406,15 +4416,22 @@ static void vlv_force_wake_reset(struct drm_i915_private *dev_priv) static void vlv_force_wake_get(struct drm_i915_private *dev_priv) { - if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL) == 0, FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL)); + I915_WRITE_NOTRACE(FORCEWAKE_MEDIA_VLV, + _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL)); - if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL), FORCEWAKE_ACK_TIMEOUT_MS)) - DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); + DRM_ERROR("Timed out waiting for GT to ack forcewake request.\n"); + + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_MEDIA_VLV) & + FORCEWAKE_KERNEL), + FORCEWAKE_ACK_TIMEOUT_MS)) + DRM_ERROR("Timed out waiting for media to ack forcewake request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4422,8 +4439,9 @@ static void vlv_force_wake_get(struct drm_i915_private *dev_priv) static void vlv_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); - /* something from same cacheline, but !FORCEWAKE_VLV */ - POSTING_READ(FORCEWAKE_ACK_VLV); + I915_WRITE_NOTRACE(FORCEWAKE_MEDIA_VLV, + _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); + /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } @@ -4508,3 +4526,56 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val) return 0; } + +static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, + u8 addr, u32 *val) +{ + u32 cmd, devfn, port, be, bar; + + bar = 0; + be = 0xf; + port = IOSF_PORT_PUNIT; + devfn = PCI_DEVFN(2, 0); + + cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | + (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) | + (bar << IOSF_BAR_SHIFT); + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + if (I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) { + DRM_DEBUG_DRIVER("warning: pcode (%s) mailbox access failed\n", + opcode == PUNIT_OPCODE_REG_READ ? + "read" : "write"); + return -EAGAIN; + } + + I915_WRITE(VLV_IOSF_ADDR, addr); + if (opcode == PUNIT_OPCODE_REG_WRITE) + I915_WRITE(VLV_IOSF_DATA, *val); + I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); + + if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, + 500)) { + DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", + opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", + addr); + return -ETIMEDOUT; + } + + if (opcode == PUNIT_OPCODE_REG_READ) + *val = I915_READ(VLV_IOSF_DATA); + I915_WRITE(VLV_IOSF_DATA, 0); + + return 0; +} + +int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +{ + return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_READ, addr, val); +} + +int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) +{ + return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_WRITE, addr, &val); +} diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index d07a8cd..298dc85 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -246,11 +246,11 @@ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val) return; } - if (intel_sdvo->sdvo_reg == SDVOB) { - cval = I915_READ(SDVOC); - } else { - bval = I915_READ(SDVOB); - } + if (intel_sdvo->sdvo_reg == GEN3_SDVOB) + cval = I915_READ(GEN3_SDVOC); + else + bval = I915_READ(GEN3_SDVOB); + /* * Write the registers twice for luck. Sometimes, * writing them only once doesn't appear to 'stick'. @@ -258,10 +258,10 @@ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val) */ for (i = 0; i < 2; i++) { - I915_WRITE(SDVOB, bval); - I915_READ(SDVOB); - I915_WRITE(SDVOC, cval); - I915_READ(SDVOC); + I915_WRITE(GEN3_SDVOB, bval); + I915_READ(GEN3_SDVOB); + I915_WRITE(GEN3_SDVOC, cval); + I915_READ(GEN3_SDVOC); } } @@ -451,7 +451,7 @@ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, int i, ret = true; /* Would be simpler to allocate both in one go ? */ - buf = (u8 *)kzalloc(args_len * 2 + 2, GFP_KERNEL); + buf = kzalloc(args_len * 2 + 2, GFP_KERNEL); if (!buf) return false; @@ -788,7 +788,6 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd, v_sync_offset = mode->vsync_start - mode->vdisplay; mode_clock = mode->clock; - mode_clock /= intel_mode_get_pixel_multiplier(mode) ?: 1; mode_clock /= 10; dtd->part1.clock = mode_clock; @@ -957,14 +956,17 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, .len = DIP_LEN_AVI, }; uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + struct intel_crtc *intel_crtc = to_intel_crtc(intel_sdvo->base.base.crtc); if (intel_sdvo->rgb_quant_range_selectable) { - if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + if (intel_crtc->config.limited_color_range) avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; else avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; } + avi_if.body.avi.VIC = drm_match_cea_mode(adjusted_mode); + intel_dip_infoframe_csum(&avi_if); /* sdvo spec says that the ecc is handled by the hw, and it looks like @@ -1039,12 +1041,18 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo, return true; } -static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool intel_sdvo_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { - struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); - int multiplier; + struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + struct drm_display_mode *mode = &pipe_config->requested_mode; + + DRM_DEBUG_KMS("forcing bpc to 8 for SDVO\n"); + pipe_config->pipe_bpp = 8*3; + + if (HAS_PCH_SPLIT(encoder->base.dev)) + pipe_config->has_pch_encoder = true; /* We need to construct preferred input timings based on our * output timings. To do that, we have to set the output @@ -1071,37 +1079,40 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, /* Make the CRTC code factor in the SDVO pixel multiplier. The * SDVO device will factor out the multiplier during mode_set. */ - multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); - intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); + pipe_config->pixel_multiplier = + intel_sdvo_get_pixel_multiplier(adjusted_mode); + adjusted_mode->clock *= pipe_config->pixel_multiplier; if (intel_sdvo->color_range_auto) { /* See CEA-861-E - 5.1 Default Encoding Parameters */ + /* FIXME: This bit is only valid when using TMDS encoding and 8 + * bit per color mode. */ if (intel_sdvo->has_hdmi_monitor && drm_match_cea_mode(adjusted_mode) > 1) - intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + intel_sdvo->color_range = HDMI_COLOR_RANGE_16_235; else intel_sdvo->color_range = 0; } if (intel_sdvo->color_range) - adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + pipe_config->limited_color_range = true; return true; } -static void intel_sdvo_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder) { - struct drm_device *dev = encoder->dev; + struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = encoder->crtc; + struct drm_crtc *crtc = intel_encoder->base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); + struct drm_display_mode *adjusted_mode = + &intel_crtc->config.adjusted_mode; + struct drm_display_mode *mode = &intel_crtc->config.requested_mode; + struct intel_sdvo *intel_sdvo = to_intel_sdvo(&intel_encoder->base); u32 sdvox; struct intel_sdvo_in_out_map in_out; struct intel_sdvo_dtd input_dtd, output_dtd; - int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); int rate; if (!mode) @@ -1161,7 +1172,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, DRM_INFO("Setting input timings on %s failed\n", SDVO_NAME(intel_sdvo)); - switch (pixel_multiplier) { + switch (intel_crtc->config.pixel_multiplier) { default: case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break; case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break; @@ -1182,10 +1193,10 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, } else { sdvox = I915_READ(intel_sdvo->sdvo_reg); switch (intel_sdvo->sdvo_reg) { - case SDVOB: + case GEN3_SDVOB: sdvox &= SDVOB_PRESERVE_MASK; break; - case SDVOC: + case GEN3_SDVOC: sdvox &= SDVOC_PRESERVE_MASK; break; } @@ -1193,9 +1204,9 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, } if (INTEL_PCH_TYPE(dev) >= PCH_CPT) - sdvox |= TRANSCODER_CPT(intel_crtc->pipe); + sdvox |= SDVO_PIPE_SEL_CPT(intel_crtc->pipe); else - sdvox |= TRANSCODER(intel_crtc->pipe); + sdvox |= SDVO_PIPE_SEL(intel_crtc->pipe); if (intel_sdvo->has_hdmi_audio) sdvox |= SDVO_AUDIO_ENABLE; @@ -1205,7 +1216,8 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { /* done in crtc_mode_set as it lives inside the dpll register */ } else { - sdvox |= (pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT; + sdvox |= (intel_crtc->config.pixel_multiplier - 1) + << SDVO_PORT_MULTIPLY_SHIFT; } if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL && @@ -1219,8 +1231,12 @@ static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector) struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(&connector->base); struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base); + struct drm_i915_private *dev_priv = intel_sdvo->base.base.dev->dev_private; u16 active_outputs; + if (!(I915_READ(intel_sdvo->sdvo_reg) & SDVO_ENABLE)) + return false; + intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs); if (active_outputs & intel_sdvo_connector->output_flag) @@ -1305,15 +1321,9 @@ static void intel_enable_sdvo(struct intel_encoder *encoder) temp = I915_READ(intel_sdvo->sdvo_reg); if ((temp & SDVO_ENABLE) == 0) { /* HW workaround for IBX, we need to move the port - * to transcoder A before disabling it. */ - if (HAS_PCH_IBX(dev)) { - struct drm_crtc *crtc = encoder->base.crtc; - int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; - - /* Restore the transcoder select bit. */ - if (pipe == PIPE_B) - temp |= SDVO_PIPE_B_SELECT; - } + * to transcoder A before disabling it, so restore it here. */ + if (HAS_PCH_IBX(dev)) + temp |= SDVO_PIPE_SEL(intel_crtc->pipe); intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE); } @@ -1932,7 +1942,9 @@ intel_sdvo_set_property(struct drm_connector *connector, break; case INTEL_BROADCAST_RGB_LIMITED: intel_sdvo->color_range_auto = false; - intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + /* FIXME: this bit is only valid when using TMDS + * encoding and 8 bit per color mode. */ + intel_sdvo->color_range = HDMI_COLOR_RANGE_16_235; break; default: return -EINVAL; @@ -2040,11 +2052,6 @@ done: #undef CHECK_PROPERTY } -static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { - .mode_fixup = intel_sdvo_mode_fixup, - .mode_set = intel_sdvo_mode_set, -}; - static const struct drm_connector_funcs intel_sdvo_connector_funcs = { .dpms = intel_sdvo_dpms, .detect = intel_sdvo_detect, @@ -2779,9 +2786,15 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) SDVOB_HOTPLUG_INT_STATUS_I915 : SDVOC_HOTPLUG_INT_STATUS_I915; } - drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs); + /* Only enable the hotplug irq if we need it, to work around noisy + * hotplug lines. + */ + if (intel_sdvo->hotplug_active) + intel_encoder->hpd_pin = HPD_SDVO_B ? HPD_SDVO_B : HPD_SDVO_C; + intel_encoder->compute_config = intel_sdvo_compute_config; intel_encoder->disable = intel_disable_sdvo; + intel_encoder->mode_set = intel_sdvo_mode_set; intel_encoder->enable = intel_enable_sdvo; intel_encoder->get_hw_state = intel_sdvo_get_hw_state; @@ -2807,12 +2820,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) */ intel_sdvo->base.cloneable = false; - /* Only enable the hotplug irq if we need it, to work around noisy - * hotplug lines. - */ - if (intel_sdvo->hotplug_active) - dev_priv->hotplug_supported_mask |= hotplug_mask; - intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg); /* Set the input timing to the screen. Assume always input 0. */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 1b6eb76..c7d25c5 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -37,6 +37,174 @@ #include "i915_drv.h" static void +vlv_update_plane(struct drm_plane *dplane, struct drm_framebuffer *fb, + struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t x, uint32_t y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int pipe = intel_plane->pipe; + int plane = intel_plane->plane; + u32 sprctl; + unsigned long sprsurf_offset, linear_offset; + int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + + sprctl = I915_READ(SPCNTR(pipe, plane)); + + /* Mask out pixel format bits in case we change it */ + sprctl &= ~SP_PIXFORMAT_MASK; + sprctl &= ~SP_YUV_BYTE_ORDER_MASK; + sprctl &= ~SP_TILED; + + switch (fb->pixel_format) { + case DRM_FORMAT_YUYV: + sprctl |= SP_FORMAT_YUV422 | SP_YUV_ORDER_YUYV; + break; + case DRM_FORMAT_YVYU: + sprctl |= SP_FORMAT_YUV422 | SP_YUV_ORDER_YVYU; + break; + case DRM_FORMAT_UYVY: + sprctl |= SP_FORMAT_YUV422 | SP_YUV_ORDER_UYVY; + break; + case DRM_FORMAT_VYUY: + sprctl |= SP_FORMAT_YUV422 | SP_YUV_ORDER_VYUY; + break; + case DRM_FORMAT_RGB565: + sprctl |= SP_FORMAT_BGR565; + break; + case DRM_FORMAT_XRGB8888: + sprctl |= SP_FORMAT_BGRX8888; + break; + case DRM_FORMAT_ARGB8888: + sprctl |= SP_FORMAT_BGRA8888; + break; + case DRM_FORMAT_XBGR2101010: + sprctl |= SP_FORMAT_RGBX1010102; + break; + case DRM_FORMAT_ABGR2101010: + sprctl |= SP_FORMAT_RGBA1010102; + break; + case DRM_FORMAT_XBGR8888: + sprctl |= SP_FORMAT_RGBX8888; + break; + case DRM_FORMAT_ABGR8888: + sprctl |= SP_FORMAT_RGBA8888; + break; + default: + /* + * If we get here one of the upper layers failed to filter + * out the unsupported plane formats + */ + BUG(); + break; + } + + if (obj->tiling_mode != I915_TILING_NONE) + sprctl |= SP_TILED; + + sprctl |= SP_ENABLE; + + /* Sizes are 0 based */ + src_w--; + src_h--; + crtc_w--; + crtc_h--; + + intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size); + + I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); + I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); + + linear_offset = y * fb->pitches[0] + x * pixel_size; + sprsurf_offset = intel_gen4_compute_page_offset(&x, &y, + obj->tiling_mode, + pixel_size, + fb->pitches[0]); + linear_offset -= sprsurf_offset; + + if (obj->tiling_mode != I915_TILING_NONE) + I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x); + else + I915_WRITE(SPLINOFF(pipe, plane), linear_offset); + + I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w); + I915_WRITE(SPCNTR(pipe, plane), sprctl); + I915_MODIFY_DISPBASE(SPSURF(pipe, plane), obj->gtt_offset + + sprsurf_offset); + POSTING_READ(SPSURF(pipe, plane)); +} + +static void +vlv_disable_plane(struct drm_plane *dplane) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int pipe = intel_plane->pipe; + int plane = intel_plane->plane; + + I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) & + ~SP_ENABLE); + /* Activate double buffered register update */ + I915_MODIFY_DISPBASE(SPSURF(pipe, plane), 0); + POSTING_READ(SPSURF(pipe, plane)); +} + +static int +vlv_update_colorkey(struct drm_plane *dplane, + struct drm_intel_sprite_colorkey *key) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int pipe = intel_plane->pipe; + int plane = intel_plane->plane; + u32 sprctl; + + if (key->flags & I915_SET_COLORKEY_DESTINATION) + return -EINVAL; + + I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value); + I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value); + I915_WRITE(SPKEYMSK(pipe, plane), key->channel_mask); + + sprctl = I915_READ(SPCNTR(pipe, plane)); + sprctl &= ~SP_SOURCE_KEY; + if (key->flags & I915_SET_COLORKEY_SOURCE) + sprctl |= SP_SOURCE_KEY; + I915_WRITE(SPCNTR(pipe, plane), sprctl); + + POSTING_READ(SPKEYMSK(pipe, plane)); + + return 0; +} + +static void +vlv_get_colorkey(struct drm_plane *dplane, + struct drm_intel_sprite_colorkey *key) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int pipe = intel_plane->pipe; + int plane = intel_plane->plane; + u32 sprctl; + + key->min_value = I915_READ(SPKEYMINVAL(pipe, plane)); + key->max_value = I915_READ(SPKEYMAXVAL(pipe, plane)); + key->channel_mask = I915_READ(SPKEYMSK(pipe, plane)); + + sprctl = I915_READ(SPCNTR(pipe, plane)); + if (sprctl & SP_SOURCE_KEY) + key->flags = I915_SET_COLORKEY_SOURCE; + else + key->flags = I915_SET_COLORKEY_NONE; +} + +static void ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, @@ -441,6 +609,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, old_obj = intel_plane->obj; + intel_plane->crtc_x = crtc_x; + intel_plane->crtc_y = crtc_y; + intel_plane->crtc_w = crtc_w; + intel_plane->crtc_h = crtc_h; + intel_plane->src_x = src_x; + intel_plane->src_y = src_y; + intel_plane->src_w = src_w; + intel_plane->src_h = src_h; + src_w = src_w >> 16; src_h = src_h >> 16; @@ -513,6 +690,11 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_lock(&dev->struct_mutex); + /* Note that this will apply the VT-d workaround for scanouts, + * which is more restrictive than required for sprites. (The + * primary plane requires 256KiB alignment with 64 PTE padding, + * the sprite planes only require 128KiB alignment and 32 PTE padding. + */ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); if (ret) goto out_unlock; @@ -568,6 +750,8 @@ intel_disable_plane(struct drm_plane *plane) if (!intel_plane->obj) goto out; + intel_wait_for_vblank(dev, intel_plane->pipe); + mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(intel_plane->obj); intel_plane->obj = NULL; @@ -647,6 +831,20 @@ out_unlock: return ret; } +void intel_plane_restore(struct drm_plane *plane) +{ + struct intel_plane *intel_plane = to_intel_plane(plane); + + if (!plane->crtc || !plane->fb) + return; + + intel_update_plane(plane, plane->crtc, plane->fb, + intel_plane->crtc_x, intel_plane->crtc_y, + intel_plane->crtc_w, intel_plane->crtc_h, + intel_plane->src_x, intel_plane->src_y, + intel_plane->src_w, intel_plane->src_h); +} + static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, @@ -670,8 +868,22 @@ static uint32_t snb_plane_formats[] = { DRM_FORMAT_VYUY, }; +static uint32_t vlv_plane_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, +}; + int -intel_plane_init(struct drm_device *dev, enum pipe pipe) +intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) { struct intel_plane *intel_plane; unsigned long possible_crtcs; @@ -710,14 +922,26 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) intel_plane->can_scale = false; else intel_plane->can_scale = true; - intel_plane->max_downscale = 2; - intel_plane->update_plane = ivb_update_plane; - intel_plane->disable_plane = ivb_disable_plane; - intel_plane->update_colorkey = ivb_update_colorkey; - intel_plane->get_colorkey = ivb_get_colorkey; - - plane_formats = snb_plane_formats; - num_plane_formats = ARRAY_SIZE(snb_plane_formats); + + if (IS_VALLEYVIEW(dev)) { + intel_plane->max_downscale = 1; + intel_plane->update_plane = vlv_update_plane; + intel_plane->disable_plane = vlv_disable_plane; + intel_plane->update_colorkey = vlv_update_colorkey; + intel_plane->get_colorkey = vlv_get_colorkey; + + plane_formats = vlv_plane_formats; + num_plane_formats = ARRAY_SIZE(vlv_plane_formats); + } else { + intel_plane->max_downscale = 2; + intel_plane->update_plane = ivb_update_plane; + intel_plane->disable_plane = ivb_disable_plane; + intel_plane->update_colorkey = ivb_update_colorkey; + intel_plane->get_colorkey = ivb_get_colorkey; + + plane_formats = snb_plane_formats; + num_plane_formats = ARRAY_SIZE(snb_plane_formats); + } break; default: @@ -726,6 +950,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) } intel_plane->pipe = pipe; + intel_plane->plane = plane; possible_crtcs = (1 << pipe); ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs, &intel_plane_funcs, diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index d808421..6673726 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -905,11 +905,10 @@ intel_tv_mode_valid(struct drm_connector *connector, static bool -intel_tv_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +intel_tv_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); + struct intel_tv *intel_tv = enc_to_intel_tv(&encoder->base); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); if (!tv_mode) @@ -918,7 +917,10 @@ intel_tv_mode_fixup(struct drm_encoder *encoder, if (intel_encoder_check_is_cloned(&intel_tv->base)) return false; - adjusted_mode->clock = tv_mode->clock; + pipe_config->adjusted_mode.clock = tv_mode->clock; + DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); + pipe_config->pipe_bpp = 8*3; + return true; } @@ -1485,7 +1487,6 @@ out: } static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { - .mode_fixup = intel_tv_mode_fixup, .mode_set = intel_tv_mode_set, }; @@ -1620,6 +1621,7 @@ intel_tv_init(struct drm_device *dev) drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs, DRM_MODE_ENCODER_TVDAC); + intel_encoder->compute_config = intel_tv_compute_config; intel_encoder->enable = intel_enable_tv; intel_encoder->disable = intel_disable_tv; intel_encoder->get_hw_state = intel_tv_get_hw_state; diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig index d630134..b487cde 100644 --- a/drivers/gpu/drm/mgag200/Kconfig +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -1,6 +1,6 @@ config DRM_MGAG200 tristate "Kernel modesetting driver for MGA G200 server engines" - depends on DRM && PCI && EXPERIMENTAL + depends on DRM && PCI select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 5ea5033..dcfc973 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -112,7 +112,6 @@ struct mga_framebuffer { struct mga_fbdev { struct drm_fb_helper helper; struct mga_framebuffer mfb; - struct list_head fbdev_list; void *sysram; int size; struct ttm_bo_kmap_obj mapping; @@ -216,7 +215,7 @@ mgag200_bo(struct ttm_buffer_object *bo) { return container_of(bo, struct mgag200_bo, bo); } - /* mga_crtc.c */ + /* mgag200_crtc.c */ void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, @@ -226,7 +225,7 @@ void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, int mgag200_modeset_init(struct mga_device *mdev); void mgag200_modeset_fini(struct mga_device *mdev); - /* mga_fbdev.c */ + /* mgag200_fb.c */ int mgag200_fbdev_init(struct mga_device *mdev); void mgag200_fbdev_fini(struct mga_device *mdev); @@ -255,7 +254,7 @@ mgag200_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); - /* mga_i2c.c */ + /* mgag200_i2c.c */ struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev); void mgag200_i2c_destroy(struct mga_i2c_chan *i2c); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index d2253f6..2ebe0f6 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -249,7 +249,7 @@ int mgag200_fbdev_init(struct mga_device *mdev) struct mga_fbdev *mfbdev; int ret; - mfbdev = kzalloc(sizeof(struct mga_fbdev), GFP_KERNEL); + mfbdev = devm_kzalloc(mdev->dev->dev, sizeof(struct mga_fbdev), GFP_KERNEL); if (!mfbdev) return -ENOMEM; @@ -258,10 +258,9 @@ int mgag200_fbdev_init(struct mga_device *mdev) ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, mdev->num_crtc, MGAG200FB_CONN_LIMIT); - if (ret) { - kfree(mfbdev); + if (ret) return ret; - } + drm_fb_helper_single_add_all_connectors(&mfbdev->helper); /* disable all the possible outputs/crtcs before entering KMS mode */ @@ -278,6 +277,4 @@ void mgag200_fbdev_fini(struct mga_device *mdev) return; mga_fbdev_destroy(mdev->dev, mdev->mfbdev); - kfree(mdev->mfbdev); - mdev->mfbdev = NULL; } diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c index 5a88ec5..d3dcf54 100644 --- a/drivers/gpu/drm/mgag200/mgag200_i2c.c +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -92,6 +92,7 @@ struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) int ret; int data, clock; + WREG_DAC(MGA1064_GEN_IO_CTL2, 1); WREG_DAC(MGA1064_GEN_IO_DATA, 0xff); WREG_DAC(MGA1064_GEN_IO_CTL, 0); diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 64297c7..1f7ea05 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -76,15 +76,6 @@ static const struct drm_mode_config_funcs mga_mode_funcs = { .fb_create = mgag200_user_framebuffer_create, }; -/* Unmap the framebuffer from the core and release the memory */ -static void mga_vram_fini(struct mga_device *mdev) -{ - pci_iounmap(mdev->dev->pdev, mdev->rmmio); - mdev->rmmio = NULL; - if (mdev->mc.vram_base) - release_mem_region(mdev->mc.vram_base, mdev->mc.vram_window); -} - static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem) { int offset; @@ -140,7 +131,7 @@ static int mga_vram_init(struct mga_device *mdev) remove_conflicting_framebuffers(aper, "mgafb", true); kfree(aper); - if (!request_mem_region(mdev->mc.vram_base, mdev->mc.vram_window, + if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window, "mgadrmfb_vram")) { DRM_ERROR("can't reserve VRAM\n"); return -ENXIO; @@ -173,13 +164,13 @@ static int mgag200_device_init(struct drm_device *dev, mdev->rmmio_base = pci_resource_start(mdev->dev->pdev, 1); mdev->rmmio_size = pci_resource_len(mdev->dev->pdev, 1); - if (!request_mem_region(mdev->rmmio_base, mdev->rmmio_size, + if (!devm_request_mem_region(mdev->dev->dev, mdev->rmmio_base, mdev->rmmio_size, "mgadrmfb_mmio")) { DRM_ERROR("can't reserve mmio registers\n"); return -ENOMEM; } - mdev->rmmio = pci_iomap(dev->pdev, 1, 0); + mdev->rmmio = pcim_iomap(dev->pdev, 1, 0); if (mdev->rmmio == NULL) return -ENOMEM; @@ -188,10 +179,8 @@ static int mgag200_device_init(struct drm_device *dev, mdev->reg_1e24 = RREG32(0x1e24); ret = mga_vram_init(mdev); - if (ret) { - release_mem_region(mdev->rmmio_base, mdev->rmmio_size); + if (ret) return ret; - } mdev->bpp_shifts[0] = 0; mdev->bpp_shifts[1] = 1; @@ -200,12 +189,6 @@ static int mgag200_device_init(struct drm_device *dev, return 0; } -void mgag200_device_fini(struct mga_device *mdev) -{ - release_mem_region(mdev->rmmio_base, mdev->rmmio_size); - mga_vram_fini(mdev); -} - /* * Functions here will be called by the core once it's bound the driver to * a PCI device @@ -217,7 +200,7 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags) struct mga_device *mdev; int r; - mdev = kzalloc(sizeof(struct mga_device), GFP_KERNEL); + mdev = devm_kzalloc(dev->dev, sizeof(struct mga_device), GFP_KERNEL); if (mdev == NULL) return -ENOMEM; dev->dev_private = (void *)mdev; @@ -258,8 +241,6 @@ int mgag200_driver_unload(struct drm_device *dev) mgag200_fbdev_fini(mdev); drm_mode_config_cleanup(dev); mgag200_mm_fini(mdev); - mgag200_device_fini(mdev); - kfree(mdev); dev->dev_private = NULL; return 0; } diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index d3d99a2..fe22bb7 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -382,19 +382,19 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock) m = n = p = 0; vcomax = 800000; vcomin = 400000; - pllreffreq = 3333; + pllreffreq = 33333; delta = 0xffffffff; permitteddelta = clock * 5 / 1000; - for (testp = 16; testp > 0; testp--) { + for (testp = 16; testp > 0; testp >>= 1) { if (clock * testp > vcomax) continue; if (clock * testp < vcomin) continue; for (testm = 1; testm < 33; testm++) { - for (testn = 1; testn < 257; testn++) { + for (testn = 17; testn < 257; testn++) { computed = (pllreffreq * testn) / (testm * testp); if (computed > clock) @@ -404,11 +404,11 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock) if (tmpdelta < delta) { delta = tmpdelta; n = testn - 1; - m = (testm - 1) | ((n >> 1) & 0x80); + m = (testm - 1); p = testp - 1; } if ((clock * testp) >= 600000) - p |= 80; + p |= 0x80; } } } @@ -1406,6 +1406,14 @@ static int mga_vga_get_modes(struct drm_connector *connector) static int mga_vga_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + struct drm_device *dev = connector->dev; + struct mga_device *mdev = (struct mga_device*)dev->dev_private; + struct mga_fbdev *mfbdev = mdev->mfbdev; + struct drm_fb_helper *fb_helper = &mfbdev->helper; + struct drm_fb_helper_connector *fb_helper_conn = NULL; + int bpp = 32; + int i = 0; + /* FIXME: Add bandwidth and g200se limitations */ if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 || @@ -1415,6 +1423,25 @@ static int mga_vga_mode_valid(struct drm_connector *connector, return MODE_BAD; } + /* Validate the mode input by the user */ + for (i = 0; i < fb_helper->connector_count; i++) { + if (fb_helper->connector_info[i]->connector == connector) { + /* Found the helper for this connector */ + fb_helper_conn = fb_helper->connector_info[i]; + if (fb_helper_conn->cmdline_mode.specified) { + if (fb_helper_conn->cmdline_mode.bpp_specified) { + bpp = fb_helper_conn->cmdline_mode.bpp; + } + } + } + } + + if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->mc.vram_size) { + if (fb_helper_conn) + fb_helper_conn->cmdline_mode.specified = false; + return MODE_BAD; + } + return MODE_OK; } diff --git a/drivers/gpu/drm/nouveau/core/core/falcon.c b/drivers/gpu/drm/nouveau/core/core/falcon.c index 6b0843c..e05c157 100644 --- a/drivers/gpu/drm/nouveau/core/core/falcon.c +++ b/drivers/gpu/drm/nouveau/core/core/falcon.c @@ -73,8 +73,11 @@ _nouveau_falcon_init(struct nouveau_object *object) nv_debug(falcon, "data limit: %d\n", falcon->data.limit); /* wait for 'uc halted' to be signalled before continuing */ - if (falcon->secret) { - nv_wait(falcon, 0x008, 0x00000010, 0x00000010); + if (falcon->secret && falcon->version < 4) { + if (!falcon->version) + nv_wait(falcon, 0x008, 0x00000010, 0x00000010); + else + nv_wait(falcon, 0x180, 0x80000000, 0); nv_wo32(falcon, 0x004, 0x00000010); } diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c index 0daab62..3b2e7b63 100644 --- a/drivers/gpu/drm/nouveau/core/core/object.c +++ b/drivers/gpu/drm/nouveau/core/core/object.c @@ -278,7 +278,6 @@ nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle) struct nouveau_object *parent = NULL; struct nouveau_object *namedb = NULL; struct nouveau_handle *handle = NULL; - int ret = -EINVAL; parent = nouveau_handle_ref(client, _parent); if (!parent) @@ -295,7 +294,7 @@ nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle) } nouveau_object_ref(NULL, &parent); - return ret; + return handle ? 0 : -EINVAL; } int diff --git a/drivers/gpu/drm/nouveau/core/core/subdev.c b/drivers/gpu/drm/nouveau/core/core/subdev.c index f74c30a..48f0637 100644 --- a/drivers/gpu/drm/nouveau/core/core/subdev.c +++ b/drivers/gpu/drm/nouveau/core/core/subdev.c @@ -99,7 +99,7 @@ nouveau_subdev_create_(struct nouveau_object *parent, if (ret) return ret; - mutex_init(&subdev->mutex); + __mutex_init(&subdev->mutex, subname, &oclass->lock_class_key); subdev->name = subname; if (parent) { diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 5fa1326..02e369f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -544,13 +544,13 @@ nv50_disp_curs_ofuncs = { static void nv50_disp_base_vblank_enable(struct nouveau_event *event, int head) { - nv_mask(event->priv, 0x61002c, (1 << head), (1 << head)); + nv_mask(event->priv, 0x61002c, (4 << head), (4 << head)); } static void nv50_disp_base_vblank_disable(struct nouveau_event *event, int head) { - nv_mask(event->priv, 0x61002c, (1 << head), (0 << head)); + nv_mask(event->priv, 0x61002c, (4 << head), 0); } static int diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 61cec0f..4857f91 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -350,7 +350,7 @@ nve0_graph_init_gpc_0(struct nvc0_graph_priv *priv) nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); } - nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); } diff --git a/drivers/gpu/drm/nouveau/core/include/core/object.h b/drivers/gpu/drm/nouveau/core/include/core/object.h index 6a90267..62e68ba 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/object.h +++ b/drivers/gpu/drm/nouveau/core/include/core/object.h @@ -50,10 +50,13 @@ int nouveau_object_fini(struct nouveau_object *, bool suspend); extern struct nouveau_ofuncs nouveau_object_ofuncs; +/* Don't allocate dynamically, because lockdep needs lock_class_keys to be in + * ".data". */ struct nouveau_oclass { u32 handle; - struct nouveau_ofuncs *ofuncs; - struct nouveau_omthds *omthds; + struct nouveau_ofuncs * const ofuncs; + struct nouveau_omthds * const omthds; + struct lock_class_key lock_class_key; }; #define nv_oclass(o) nv_object(o)->oclass diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h index 6b17b61..0b20fc0 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h @@ -4,7 +4,7 @@ #include <core/device.h> #include <core/subdev.h> -enum nouveau_therm_mode { +enum nouveau_therm_fan_mode { NOUVEAU_THERM_CTRL_NONE = 0, NOUVEAU_THERM_CTRL_MANUAL = 1, NOUVEAU_THERM_CTRL_AUTO = 2, diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 2cc1e6a..9c41b58 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -869,7 +869,7 @@ init_idx_addr_latched(struct nvbios_init *init) init->offset += 2; init_wr32(init, dreg, idata); - init_mask(init, creg, ~mask, data | idata); + init_mask(init, creg, ~mask, data | iaddr); } } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c index d6d1600..d62045f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c @@ -86,8 +86,8 @@ nouveau_fb_preinit(struct nouveau_fb *pfb) return ret; } - if (!nouveau_mm_initialised(&pfb->tags) && tags) { - ret = nouveau_mm_init(&pfb->tags, 0, ++tags, 1); + if (!nouveau_mm_initialised(&pfb->tags)) { + ret = nouveau_mm_init(&pfb->tags, 0, tags ? ++tags : 0, 1); if (ret) return ret; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c index a4338d9..0772ec9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c @@ -101,7 +101,7 @@ nv50_fb_vram_init(struct nouveau_fb *pfb) struct nouveau_bios *bios = nouveau_bios(device); const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ - u32 size; + u32 size, tags = 0; int ret; pfb->ram.size = nv_rd32(pfb, 0x10020c); @@ -142,10 +142,11 @@ nv50_fb_vram_init(struct nouveau_fb *pfb) return ret; pfb->ram.ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; + tags = nv_rd32(pfb, 0x100320); break; } - return nv_rd32(pfb, 0x100320); + return tags; } static int diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index a114a0e..2e98e8a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -142,6 +142,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, /* drop port's i2c subdev refcount, i2c handles this itself */ if (ret == 0) { list_add_tail(&port->head, &i2c->ports); + atomic_dec(&parent->refcount); atomic_dec(&engine->refcount); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c index f794dc8..a00a5a7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c @@ -134,7 +134,7 @@ nouveau_therm_alarm(struct nouveau_alarm *alarm) } int -nouveau_therm_mode(struct nouveau_therm *therm, int mode) +nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode) { struct nouveau_therm_priv *priv = (void *)therm; struct nouveau_device *device = nv_device(therm); @@ -149,10 +149,15 @@ nouveau_therm_mode(struct nouveau_therm *therm, int mode) (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0)) return -EINVAL; + /* do not allow automatic fan management if the thermal sensor is + * not available */ + if (priv->mode == 2 && therm->temp_get(therm) < 0) + return -EINVAL; + if (priv->mode == mode) return 0; - nv_info(therm, "Thermal management: %s\n", name[mode]); + nv_info(therm, "fan management: %s\n", name[mode]); nouveau_therm_update(therm, mode); return 0; } @@ -213,7 +218,7 @@ nouveau_therm_attr_set(struct nouveau_therm *therm, priv->fan->bios.max_duty = value; return 0; case NOUVEAU_THERM_ATTR_FAN_MODE: - return nouveau_therm_mode(therm, value); + return nouveau_therm_fan_mode(therm, value); case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST: priv->bios_sensor.thrs_fan_boost.temp = value; priv->sensor.program_alarms(therm); @@ -263,7 +268,7 @@ _nouveau_therm_init(struct nouveau_object *object) return ret; if (priv->suspend >= 0) - nouveau_therm_mode(therm, priv->mode); + nouveau_therm_fan_mode(therm, priv->mode); priv->sensor.program_alarms(therm); return 0; } @@ -313,11 +318,12 @@ nouveau_therm_create_(struct nouveau_object *parent, int nouveau_therm_preinit(struct nouveau_therm *therm) { - nouveau_therm_ic_ctor(therm); nouveau_therm_sensor_ctor(therm); + nouveau_therm_ic_ctor(therm); nouveau_therm_fan_ctor(therm); - nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_NONE); + nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_NONE); + nouveau_therm_sensor_preinit(therm); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c index e24090b..8b3adec 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c @@ -32,6 +32,7 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c, struct i2c_board_info *info) { struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c); + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; struct i2c_client *client; request_module("%s%s", I2C_MODULE_PREFIX, info->type); @@ -46,8 +47,9 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c, } nv_info(priv, - "Found an %s at address 0x%x (controlled by lm_sensors)\n", - info->type, info->addr); + "Found an %s at address 0x%x (controlled by lm_sensors, " + "temp offset %+i C)\n", + info->type, info->addr, sensor->offset_constant); priv->ic = client; return true; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c index 0f5363e..a70d1b7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c @@ -29,54 +29,83 @@ struct nv40_therm_priv { struct nouveau_therm_priv base; }; +enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 }; + +static enum nv40_sensor_style +nv40_sensor_style(struct nouveau_therm *therm) +{ + struct nouveau_device *device = nv_device(therm); + + switch (device->chipset) { + case 0x43: + case 0x44: + case 0x4a: + case 0x47: + return OLD_STYLE; + + case 0x46: + case 0x49: + case 0x4b: + case 0x4e: + case 0x4c: + case 0x67: + case 0x68: + case 0x63: + return NEW_STYLE; + default: + return INVALID_STYLE; + } +} + static int nv40_sensor_setup(struct nouveau_therm *therm) { - struct nouveau_device *device = nv_device(therm); + enum nv40_sensor_style style = nv40_sensor_style(therm); /* enable ADC readout and disable the ALARM threshold */ - if (device->chipset >= 0x46) { + if (style == NEW_STYLE) { nv_mask(therm, 0x15b8, 0x80000000, 0); nv_wr32(therm, 0x15b0, 0x80003fff); - mdelay(10); /* wait for the temperature to stabilize */ + mdelay(20); /* wait for the temperature to stabilize */ return nv_rd32(therm, 0x15b4) & 0x3fff; - } else { + } else if (style == OLD_STYLE) { nv_wr32(therm, 0x15b0, 0xff); + mdelay(20); /* wait for the temperature to stabilize */ return nv_rd32(therm, 0x15b4) & 0xff; - } + } else + return -ENODEV; } static int nv40_temp_get(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; - struct nouveau_device *device = nv_device(therm); struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + enum nv40_sensor_style style = nv40_sensor_style(therm); int core_temp; - if (device->chipset >= 0x46) { + if (style == NEW_STYLE) { nv_wr32(therm, 0x15b0, 0x80003fff); core_temp = nv_rd32(therm, 0x15b4) & 0x3fff; - } else { + } else if (style == OLD_STYLE) { nv_wr32(therm, 0x15b0, 0xff); core_temp = nv_rd32(therm, 0x15b4) & 0xff; - } - - /* Setup the sensor if the temperature is 0 */ - if (core_temp == 0) - core_temp = nv40_sensor_setup(therm); + } else + return -ENODEV; - if (sensor->slope_div == 0) - sensor->slope_div = 1; - if (sensor->offset_den == 0) - sensor->offset_den = 1; - if (sensor->slope_mult < 1) - sensor->slope_mult = 1; + /* if the slope or the offset is unset, do no use the sensor */ + if (!sensor->slope_div || !sensor->slope_mult || + !sensor->offset_num || !sensor->offset_den) + return -ENODEV; core_temp = core_temp * sensor->slope_mult / sensor->slope_div; core_temp = core_temp + sensor->offset_num / sensor->offset_den; core_temp = core_temp + sensor->offset_constant - 8; + /* reserve negative temperatures for errors */ + if (core_temp < 0) + core_temp = 0; + return core_temp; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index 06b9870..438d982 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -102,7 +102,7 @@ struct nouveau_therm_priv { struct i2c_client *ic; }; -int nouveau_therm_mode(struct nouveau_therm *therm, int mode); +int nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode); int nouveau_therm_attr_get(struct nouveau_therm *therm, enum nouveau_therm_attr_type type); int nouveau_therm_attr_set(struct nouveau_therm *therm, @@ -122,6 +122,7 @@ int nouveau_therm_fan_sense(struct nouveau_therm *therm); int nouveau_therm_preinit(struct nouveau_therm *); +void nouveau_therm_sensor_preinit(struct nouveau_therm *); void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm, enum nouveau_therm_thrs thrs, enum nouveau_therm_thrs_state st); diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c index b37624a..470f6a4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c @@ -34,10 +34,6 @@ nouveau_therm_temp_set_defaults(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; - priv->bios_sensor.slope_mult = 1; - priv->bios_sensor.slope_div = 1; - priv->bios_sensor.offset_num = 0; - priv->bios_sensor.offset_den = 1; priv->bios_sensor.offset_constant = 0; priv->bios_sensor.thrs_fan_boost.temp = 90; @@ -60,11 +56,6 @@ nouveau_therm_temp_safety_checks(struct nouveau_therm *therm) struct nouveau_therm_priv *priv = (void *)therm; struct nvbios_therm_sensor *s = &priv->bios_sensor; - if (!priv->bios_sensor.slope_div) - priv->bios_sensor.slope_div = 1; - if (!priv->bios_sensor.offset_den) - priv->bios_sensor.offset_den = 1; - /* enforce a minimum hysteresis on thresholds */ s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2); s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2); @@ -106,16 +97,16 @@ void nouveau_therm_sensor_event(struct nouveau_therm *therm, const char *thresolds[] = { "fanboost", "downclock", "critical", "shutdown" }; - uint8_t temperature = therm->temp_get(therm); + int temperature = therm->temp_get(therm); if (thrs < 0 || thrs > 3) return; if (dir == NOUVEAU_THERM_THRS_FALLING) - nv_info(therm, "temperature (%u C) went below the '%s' threshold\n", + nv_info(therm, "temperature (%i C) went below the '%s' threshold\n", temperature, thresolds[thrs]); else - nv_info(therm, "temperature (%u C) hit the '%s' threshold\n", + nv_info(therm, "temperature (%i C) hit the '%s' threshold\n", temperature, thresolds[thrs]); active = (dir == NOUVEAU_THERM_THRS_RISING); @@ -123,7 +114,7 @@ void nouveau_therm_sensor_event(struct nouveau_therm *therm, case NOUVEAU_THERM_THRS_FANBOOST: if (active) { nouveau_therm_fan_set(therm, true, 100); - nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_AUTO); + nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_AUTO); } break; case NOUVEAU_THERM_THRS_DOWNCLOCK: @@ -202,7 +193,7 @@ alarm_timer_callback(struct nouveau_alarm *alarm) NOUVEAU_THERM_THRS_SHUTDOWN); /* schedule the next poll in one second */ - if (list_empty(&alarm->head)) + if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head)) ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm); spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); @@ -225,6 +216,17 @@ nouveau_therm_program_alarms_polling(struct nouveau_therm *therm) alarm_timer_callback(&priv->sensor.therm_poll_alarm); } +void +nouveau_therm_sensor_preinit(struct nouveau_therm *therm) +{ + const char *sensor_avail = "yes"; + + if (therm->temp_get(therm) < 0) + sensor_avail = "no"; + + nv_info(therm, "internal sensor: %s\n", sensor_avail); +} + int nouveau_therm_sensor_ctor(struct nouveau_therm *therm) { diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 4124192..5eb3e0d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -116,6 +116,11 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16, { struct nouveau_abi16_ntfy *ntfy, *temp; + /* wait for all activity to stop before releasing notify object, which + * may be still in use */ + if (chan->chan && chan->ntfy) + nouveau_channel_idle(chan->chan); + /* cleanup notifier state */ list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) { nouveau_abi16_ntfy_fini(chan, ntfy); @@ -386,7 +391,7 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_device *device = nv_device(drm->device); struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); - struct nouveau_abi16_chan *chan, *temp; + struct nouveau_abi16_chan *chan = NULL, *temp; struct nouveau_abi16_ntfy *ntfy; struct nouveau_object *object; struct nv_dma_class args = {}; @@ -399,10 +404,11 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) if (unlikely(nv_device(abi16->device)->card_type >= NV_C0)) return nouveau_abi16_put(abi16, -EINVAL); - list_for_each_entry_safe(chan, temp, &abi16->channels, head) { - if (chan->chan->handle == (NVDRM_CHAN | info->channel)) + list_for_each_entry(temp, &abi16->channels, head) { + if (temp->chan->handle == (NVDRM_CHAN | info->channel)) { + chan = temp; break; - chan = NULL; + } } if (!chan) @@ -454,17 +460,18 @@ nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS) { struct drm_nouveau_gpuobj_free *fini = data; struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); - struct nouveau_abi16_chan *chan, *temp; + struct nouveau_abi16_chan *chan = NULL, *temp; struct nouveau_abi16_ntfy *ntfy; int ret; if (unlikely(!abi16)) return -ENOMEM; - list_for_each_entry_safe(chan, temp, &abi16->channels, head) { - if (chan->chan->handle == (NVDRM_CHAN | fini->channel)) + list_for_each_entry(temp, &abi16->channels, head) { + if (temp->chan->handle == (NVDRM_CHAN | fini->channel)) { + chan = temp; break; - chan = NULL; + } } if (!chan) diff --git a/drivers/gpu/drm/nouveau/nouveau_agp.c b/drivers/gpu/drm/nouveau/nouveau_agp.c index d28430c..6e7a55f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_agp.c +++ b/drivers/gpu/drm/nouveau/nouveau_agp.c @@ -47,6 +47,18 @@ nouveau_agp_enabled(struct nouveau_drm *drm) if (drm->agp.stat == UNKNOWN) { if (!nouveau_agpmode) return false; +#ifdef __powerpc__ + /* Disable AGP by default on all PowerPC machines for + * now -- At least some UniNorth-2 AGP bridges are + * known to be broken: DMA from the host to the card + * works just fine, but writeback from the card to the + * host goes straight to memory untranslated bypassing + * the GATT somehow, making them quite painful to deal + * with... + */ + if (nouveau_agpmode == -1) + return false; +#endif return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 11ca821..7ff1071 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -801,7 +801,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, stride = 16 * 4; height = amount / stride; - if (new_mem->mem_type == TTM_PL_VRAM && + if (old_mem->mem_type == TTM_PL_VRAM && nouveau_bo_tile_layout(nvbo)) { ret = RING_SPACE(chan, 8); if (ret) @@ -823,7 +823,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, BEGIN_NV04(chan, NvSubCopy, 0x0200, 1); OUT_RING (chan, 1); } - if (old_mem->mem_type == TTM_PL_VRAM && + if (new_mem->mem_type == TTM_PL_VRAM && nouveau_bo_tile_layout(nvbo)) { ret = RING_SPACE(chan, 8); if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8a03c58..c95decf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -72,11 +72,25 @@ module_param_named(modeset, nouveau_modeset, int, 0400); static struct drm_driver driver; static int +nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head) +{ + struct nouveau_drm *drm = + container_of(event, struct nouveau_drm, vblank[head]); + drm_handle_vblank(drm->dev, head); + return NVKM_EVENT_KEEP; +} + +static int nouveau_drm_vblank_enable(struct drm_device *dev, int head) { struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_disp *pdisp = nouveau_disp(drm->device); - nouveau_event_get(pdisp->vblank, head, &drm->vblank); + + if (WARN_ON_ONCE(head > ARRAY_SIZE(drm->vblank))) + return -EIO; + WARN_ON_ONCE(drm->vblank[head].func); + drm->vblank[head].func = nouveau_drm_vblank_handler; + nouveau_event_get(pdisp->vblank, head, &drm->vblank[head]); return 0; } @@ -85,16 +99,11 @@ nouveau_drm_vblank_disable(struct drm_device *dev, int head) { struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_disp *pdisp = nouveau_disp(drm->device); - nouveau_event_put(pdisp->vblank, head, &drm->vblank); -} - -static int -nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head) -{ - struct nouveau_drm *drm = - container_of(event, struct nouveau_drm, vblank); - drm_handle_vblank(drm->dev, head); - return NVKM_EVENT_KEEP; + if (drm->vblank[head].func) + nouveau_event_put(pdisp->vblank, head, &drm->vblank[head]); + else + WARN_ON_ONCE(1); + drm->vblank[head].func = NULL; } static u64 @@ -275,6 +284,8 @@ static int nouveau_drm_probe(struct pci_dev *pdev, return 0; } +static struct lock_class_key drm_client_lock_class_key; + static int nouveau_drm_load(struct drm_device *dev, unsigned long flags) { @@ -286,10 +297,10 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm); if (ret) return ret; + lockdep_set_class(&drm->client.mutex, &drm_client_lock_class_key); dev->dev_private = drm; drm->dev = dev; - drm->vblank.func = nouveau_drm_vblank_handler; INIT_LIST_HEAD(&drm->clients); spin_lock_init(&drm->tile.lock); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index b25df37..9c39baf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -113,7 +113,7 @@ struct nouveau_drm { struct nvbios vbios; struct nouveau_display *display; struct backlight_device *backlight; - struct nouveau_eventh vblank; + struct nouveau_eventh vblank[4]; /* power management */ struct nouveau_pm *pm; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index bb54098..936b442 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -402,8 +402,12 @@ nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_therm *therm = nouveau_therm(drm->device); + int temp = therm->temp_get(therm); - return snprintf(buf, PAGE_SIZE, "%d\n", therm->temp_get(therm) * 1000); + if (temp < 0) + return temp; + + return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000); } static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, NULL, 0); @@ -871,7 +875,12 @@ static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_get_pwm1_max, nouveau_hwmon_set_pwm1_max, 0); -static struct attribute *hwmon_attributes[] = { +static struct attribute *hwmon_default_attributes[] = { + &sensor_dev_attr_name.dev_attr.attr, + &sensor_dev_attr_update_rate.dev_attr.attr, + NULL +}; +static struct attribute *hwmon_temp_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, @@ -882,8 +891,6 @@ static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, &sensor_dev_attr_temp1_emergency.dev_attr.attr, &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, - &sensor_dev_attr_name.dev_attr.attr, - &sensor_dev_attr_update_rate.dev_attr.attr, NULL }; static struct attribute *hwmon_fan_rpm_attributes[] = { @@ -898,8 +905,11 @@ static struct attribute *hwmon_pwm_fan_attributes[] = { NULL }; -static const struct attribute_group hwmon_attrgroup = { - .attrs = hwmon_attributes, +static const struct attribute_group hwmon_default_attrgroup = { + .attrs = hwmon_default_attributes, +}; +static const struct attribute_group hwmon_temp_attrgroup = { + .attrs = hwmon_temp_attributes, }; static const struct attribute_group hwmon_fan_rpm_attrgroup = { .attrs = hwmon_fan_rpm_attributes, @@ -931,13 +941,22 @@ nouveau_hwmon_init(struct drm_device *dev) } dev_set_drvdata(hwmon_dev, dev); - /* default sysfs entries */ - ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup); + /* set the default attributes */ + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup); if (ret) { if (ret) goto error; } + /* if the card has a working thermal sensor */ + if (therm->temp_get(therm) >= 0) { + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); + if (ret) { + if (ret) + goto error; + } + } + /* if the card has a pwm fan */ /*XXX: incorrect, need better detection for this, some boards have * the gpio entries for pwm fan control even when there's no @@ -979,11 +998,10 @@ nouveau_hwmon_fini(struct drm_device *dev) struct nouveau_pm *pm = nouveau_pm(dev); if (pm->hwmon) { - sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup); - sysfs_remove_group(&pm->hwmon->kobj, - &hwmon_pwm_fan_attrgroup); - sysfs_remove_group(&pm->hwmon->kobj, - &hwmon_fan_rpm_attrgroup); + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_default_attrgroup); + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_temp_attrgroup); + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup); + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup); hwmon_device_unregister(pm->hwmon); } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index a6237c9..7f0e6c3 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -55,9 +55,9 @@ /* offsets in shared sync bo of various structures */ #define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) -#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) -#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00) -#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10) +#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) +#define EVO_FLIP_SEM0(c) EVO_SYNC((c) + 1, 0x00) +#define EVO_FLIP_SEM1(c) EVO_SYNC((c) + 1, 0x10) #define EVO_CORE_HANDLE (0xd1500000) #define EVO_CHAN_HANDLE(t,i) (0xd15c0000 | (((t) & 0x00ff) << 8) | (i)) @@ -341,10 +341,8 @@ struct nv50_curs { struct nv50_sync { struct nv50_dmac base; - struct { - u32 offset; - u16 value; - } sem; + u32 addr; + u32 data; }; struct nv50_ovly { @@ -471,13 +469,33 @@ nv50_display_crtc_sema(struct drm_device *dev, int crtc) return nv50_disp(dev)->sync; } +struct nv50_display_flip { + struct nv50_disp *disp; + struct nv50_sync *chan; +}; + +static bool +nv50_display_flip_wait(void *data) +{ + struct nv50_display_flip *flip = data; + if (nouveau_bo_rd32(flip->disp->sync, flip->chan->addr / 4) == + flip->chan->data); + return true; + usleep_range(1, 2); + return false; +} + void nv50_display_flip_stop(struct drm_crtc *crtc) { - struct nv50_sync *sync = nv50_sync(crtc); + struct nouveau_device *device = nouveau_dev(crtc->dev); + struct nv50_display_flip flip = { + .disp = nv50_disp(crtc->dev), + .chan = nv50_sync(crtc), + }; u32 *push; - push = evo_wait(sync, 8); + push = evo_wait(flip.chan, 8); if (push) { evo_mthd(push, 0x0084, 1); evo_data(push, 0x00000000); @@ -487,8 +505,10 @@ nv50_display_flip_stop(struct drm_crtc *crtc) evo_data(push, 0x00000000); evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); - evo_kick(push, sync); + evo_kick(push, flip.chan); } + + nv_wait_cb(device, nv50_display_flip_wait, &flip); } int @@ -496,73 +516,78 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct nouveau_channel *chan, u32 swap_interval) { struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); - struct nv50_disp *disp = nv50_disp(crtc->dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nv50_sync *sync = nv50_sync(crtc); + int head = nv_crtc->index, ret; u32 *push; - int ret; swap_interval <<= 4; if (swap_interval == 0) swap_interval |= 0x100; + if (chan == NULL) + evo_sync(crtc->dev); push = evo_wait(sync, 128); if (unlikely(push == NULL)) return -EBUSY; - /* synchronise with the rendering channel, if necessary */ - if (likely(chan)) { + if (chan && nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) { + ret = RING_SPACE(chan, 8); + if (ret) + return ret; + + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); + OUT_RING (chan, NvEvoSema0 + head); + OUT_RING (chan, sync->addr ^ 0x10); + BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); + OUT_RING (chan, sync->data + 1); + BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); + OUT_RING (chan, sync->addr); + OUT_RING (chan, sync->data); + } else + if (chan && nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { + u64 addr = nv84_fence_crtc(chan, head) + sync->addr; + ret = RING_SPACE(chan, 12); + if (ret) + return ret; + + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + OUT_RING (chan, chan->vram); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr ^ 0x10)); + OUT_RING (chan, lower_32_bits(addr ^ 0x10)); + OUT_RING (chan, sync->data + 1); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr)); + OUT_RING (chan, lower_32_bits(addr)); + OUT_RING (chan, sync->data); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL); + } else + if (chan) { + u64 addr = nv84_fence_crtc(chan, head) + sync->addr; ret = RING_SPACE(chan, 10); if (ret) return ret; - if (nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) { - BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); - OUT_RING (chan, NvEvoSema0 + nv_crtc->index); - OUT_RING (chan, sync->sem.offset); - BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); - OUT_RING (chan, 0xf00d0000 | sync->sem.value); - BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); - OUT_RING (chan, sync->sem.offset ^ 0x10); - OUT_RING (chan, 0x74b1e000); - BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); - OUT_RING (chan, NvSema); - } else - if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { - u64 offset = nv84_fence_crtc(chan, nv_crtc->index); - offset += sync->sem.offset; - - BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 0xf00d0000 | sync->sem.value); - OUT_RING (chan, 0x00000002); - BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset ^ 0x10)); - OUT_RING (chan, 0x74b1e000); - OUT_RING (chan, 0x00000001); - } else { - u64 offset = nv84_fence_crtc(chan, nv_crtc->index); - offset += sync->sem.offset; - - BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 0xf00d0000 | sync->sem.value); - OUT_RING (chan, 0x00001002); - BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset ^ 0x10)); - OUT_RING (chan, 0x74b1e000); - OUT_RING (chan, 0x00001001); - } + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr ^ 0x10)); + OUT_RING (chan, lower_32_bits(addr ^ 0x10)); + OUT_RING (chan, sync->data + 1); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG | + NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr)); + OUT_RING (chan, lower_32_bits(addr)); + OUT_RING (chan, sync->data); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL | + NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); + } + if (chan) { + sync->addr ^= 0x10; + sync->data++; FIRE_RING (chan); - } else { - nouveau_bo_wr32(disp->sync, sync->sem.offset / 4, - 0xf00d0000 | sync->sem.value); - evo_sync(crtc->dev); } /* queue the flip */ @@ -575,9 +600,9 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, evo_data(push, 0x40000000); } evo_mthd(push, 0x0088, 4); - evo_data(push, sync->sem.offset); - evo_data(push, 0xf00d0000 | sync->sem.value); - evo_data(push, 0x74b1e000); + evo_data(push, sync->addr); + evo_data(push, sync->data++); + evo_data(push, sync->data); evo_data(push, NvEvoSync); evo_mthd(push, 0x00a0, 2); evo_data(push, 0x00000000); @@ -605,9 +630,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); evo_kick(push, sync); - - sync->sem.offset ^= 0x10; - sync->sem.value++; return 0; } @@ -1379,7 +1401,8 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index) if (ret) goto out; - head->sync.sem.offset = EVO_SYNC(1 + index, 0x00); + head->sync.addr = EVO_FLIP_SEM0(index); + head->sync.data = 0x00000000; /* allocate overlay resources */ ret = nv50_pioc_create(disp->core, NV50_DISP_OIMM_CLASS, index, @@ -2112,15 +2135,23 @@ nv50_display_fini(struct drm_device *dev) int nv50_display_init(struct drm_device *dev) { - u32 *push = evo_wait(nv50_mast(dev), 32); - if (push) { - evo_mthd(push, 0x0088, 1); - evo_data(push, NvEvoSync); - evo_kick(push, nv50_mast(dev)); - return 0; + struct nv50_disp *disp = nv50_disp(dev); + struct drm_crtc *crtc; + u32 *push; + + push = evo_wait(nv50_mast(dev), 32); + if (!push) + return -EBUSY; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nv50_sync *sync = nv50_sync(crtc); + nouveau_bo_wr32(disp->sync, sync->addr / 4, sync->data); } - return -EBUSY; + evo_mthd(push, 0x0088, 1); + evo_data(push, NvEvoSync); + evo_kick(push, nv50_mast(dev)); + return 0; } void @@ -2245,6 +2276,7 @@ nv50_display_create(struct drm_device *dev) NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", dcbe->location, dcbe->type, ffs(dcbe->or) - 1, ret); + ret = 0; } } diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig index b724a41..09f65dc 100644 --- a/drivers/gpu/drm/omapdrm/Kconfig +++ b/drivers/gpu/drm/omapdrm/Kconfig @@ -3,8 +3,8 @@ config DRM_OMAP tristate "OMAP DRM" depends on DRM && !CONFIG_FB_OMAP2 depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM + depends on OMAP2_DSS select DRM_KMS_HELPER - select OMAP2_DSS select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 44284fd..912759d 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -110,6 +110,11 @@ static enum drm_connector_status omap_connector_detect( ret = connector_status_connected; else ret = connector_status_disconnected; + } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI || + dssdev->type == OMAP_DISPLAY_TYPE_DBI || + dssdev->type == OMAP_DISPLAY_TYPE_SDI || + dssdev->type == OMAP_DISPLAY_TYPE_DSI) { + ret = connector_status_connected; } else { ret = connector_status_unknown; } @@ -189,12 +194,30 @@ static int omap_connector_mode_valid(struct drm_connector *connector, struct omap_video_timings timings = {0}; struct drm_device *dev = connector->dev; struct drm_display_mode *new_mode; - int ret = MODE_BAD; + int r, ret = MODE_BAD; copy_timings_drm_to_omap(&timings, mode); mode->vrefresh = drm_mode_vrefresh(mode); - if (!dssdrv->check_timings(dssdev, &timings)) { + /* + * if the panel driver doesn't have a check_timings, it's most likely + * a fixed resolution panel, check if the timings match with the + * panel's timings + */ + if (dssdrv->check_timings) { + r = dssdrv->check_timings(dssdev, &timings); + } else { + struct omap_video_timings t = {0}; + + dssdrv->get_timings(dssdev, &t); + + if (memcmp(&timings, &t, sizeof(struct omap_video_timings))) + r = -EINVAL; + else + r = 0; + } + + if (!r) { /* check if vrefresh is still valid */ new_mode = drm_mode_duplicate(dev, mode); new_mode->clock = timings.pixel_clock; @@ -261,10 +284,8 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, omap_dss_get_device(dssdev); omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); - if (!omap_connector) { - dev_err(dev->dev, "could not allocate connector\n"); + if (!omap_connector) goto fail; - } omap_connector->dssdev = dssdev; omap_connector->encoder = encoder; diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index ac2258f..79b200a 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -74,6 +74,13 @@ struct omap_crtc { struct work_struct page_flip_work; }; +uint32_t pipe2vbl(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + return dispc_mgr_get_vsync_irq(omap_crtc->channel); +} + /* * Manager-ops, callbacks from output when they need to configure * the upstream part of the video pipe. @@ -599,11 +606,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, DBG("%s", channel_names[channel]); omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); - - if (!omap_crtc) { - dev_err(dev->dev, "could not allocate CRTC\n"); + if (!omap_crtc) goto fail; - } crtc = &omap_crtc->base; @@ -616,7 +620,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->apply.pre_apply = omap_crtc_pre_apply; omap_crtc->apply.post_apply = omap_crtc_post_apply; - omap_crtc->apply_irq.irqmask = pipe2vbl(id); + omap_crtc->channel = channel; + omap_crtc->plane = plane; + omap_crtc->plane->crtc = crtc; + omap_crtc->name = channel_names[channel]; + omap_crtc->pipe = id; + + omap_crtc->apply_irq.irqmask = pipe2vbl(crtc); omap_crtc->apply_irq.irq = omap_crtc_apply_irq; omap_crtc->error_irq.irqmask = @@ -624,12 +634,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->error_irq.irq = omap_crtc_error_irq; omap_irq_register(dev, &omap_crtc->error_irq); - omap_crtc->channel = channel; - omap_crtc->plane = plane; - omap_crtc->plane->crtc = crtc; - omap_crtc->name = channel_names[channel]; - omap_crtc->pipe = id; - /* temporary: */ omap_crtc->mgr.id = channel; diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 3910215..9b794c9 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -581,10 +581,8 @@ static int omap_dmm_probe(struct platform_device *dev) struct resource *mem; omap_dmm = kzalloc(sizeof(*omap_dmm), GFP_KERNEL); - if (!omap_dmm) { - dev_err(&dev->dev, "failed to allocate driver data section\n"); + if (!omap_dmm) goto fail; - } /* initialize lists */ INIT_LIST_HEAD(&omap_dmm->alloc_head); @@ -681,11 +679,9 @@ static int omap_dmm_probe(struct platform_device *dev) } /* alloc engines */ - omap_dmm->engines = kzalloc( - omap_dmm->num_engines * sizeof(struct refill_engine), - GFP_KERNEL); + omap_dmm->engines = kcalloc(omap_dmm->num_engines, + sizeof(struct refill_engine), GFP_KERNEL); if (!omap_dmm->engines) { - dev_err(&dev->dev, "could not allocate engines\n"); ret = -ENOMEM; goto fail; } @@ -702,10 +698,9 @@ static int omap_dmm_probe(struct platform_device *dev) list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head); } - omap_dmm->tcm = kzalloc(omap_dmm->num_lut * sizeof(*omap_dmm->tcm), + omap_dmm->tcm = kcalloc(omap_dmm->num_lut, sizeof(*omap_dmm->tcm), GFP_KERNEL); if (!omap_dmm->tcm) { - dev_err(&dev->dev, "failed to allocate lut ptrs\n"); ret = -ENOMEM; goto fail; } diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 9083538..9c53c25 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -74,54 +74,53 @@ static int get_connector_type(struct omap_dss_device *dssdev) } } +static bool channel_used(struct drm_device *dev, enum omap_channel channel) +{ + struct omap_drm_private *priv = dev->dev_private; + int i; + + for (i = 0; i < priv->num_crtcs; i++) { + struct drm_crtc *crtc = priv->crtcs[i]; + + if (omap_crtc_channel(crtc) == channel) + return true; + } + + return false; +} + static int omap_modeset_init(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; struct omap_dss_device *dssdev = NULL; int num_ovls = dss_feat_get_num_ovls(); - int id; + int num_mgrs = dss_feat_get_num_mgrs(); + int num_crtcs; + int i, id = 0; drm_mode_config_init(dev); omap_drm_irq_install(dev); /* - * Create private planes and CRTCs for the last NUM_CRTCs overlay - * plus manager: + * We usually don't want to create a CRTC for each manager, at least + * not until we have a way to expose private planes to userspace. + * Otherwise there would not be enough video pipes left for drm planes. + * We use the num_crtc argument to limit the number of crtcs we create. */ - for (id = 0; id < min(num_crtc, num_ovls); id++) { - struct drm_plane *plane; - struct drm_crtc *crtc; - - plane = omap_plane_init(dev, id, true); - crtc = omap_crtc_init(dev, plane, pipe2chan(id), id); + num_crtcs = min3(num_crtc, num_mgrs, num_ovls); - BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); - priv->crtcs[id] = crtc; - priv->num_crtcs++; - - priv->planes[id] = plane; - priv->num_planes++; - } - - /* - * Create normal planes for the remaining overlays: - */ - for (; id < num_ovls; id++) { - struct drm_plane *plane = omap_plane_init(dev, id, false); - - BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); - priv->planes[priv->num_planes++] = plane; - } + dssdev = NULL; for_each_dss_dev(dssdev) { struct drm_connector *connector; struct drm_encoder *encoder; + enum omap_channel channel; if (!dssdev->driver) { dev_warn(dev->dev, "%s has no driver.. skipping it\n", dssdev->name); - return 0; + continue; } if (!(dssdev->driver->get_timings || @@ -129,7 +128,7 @@ static int omap_modeset_init(struct drm_device *dev) dev_warn(dev->dev, "%s driver does not support " "get_timings or read_edid.. skipping it!\n", dssdev->name); - return 0; + continue; } encoder = omap_encoder_init(dev, dssdev); @@ -157,16 +156,118 @@ static int omap_modeset_init(struct drm_device *dev) drm_mode_connector_attach_encoder(connector, encoder); + /* + * if we have reached the limit of the crtcs we are allowed to + * create, let's not try to look for a crtc for this + * panel/encoder and onwards, we will, of course, populate the + * the possible_crtcs field for all the encoders with the final + * set of crtcs we create + */ + if (id == num_crtcs) + continue; + + /* + * get the recommended DISPC channel for this encoder. For now, + * we only try to get create a crtc out of the recommended, the + * other possible channels to which the encoder can connect are + * not considered. + */ + channel = dssdev->output->dispc_channel; + + /* + * if this channel hasn't already been taken by a previously + * allocated crtc, we create a new crtc for it + */ + if (!channel_used(dev, channel)) { + struct drm_plane *plane; + struct drm_crtc *crtc; + + plane = omap_plane_init(dev, id, true); + crtc = omap_crtc_init(dev, plane, channel, id); + + BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); + priv->crtcs[id] = crtc; + priv->num_crtcs++; + + priv->planes[id] = plane; + priv->num_planes++; + + id++; + } + } + + /* + * we have allocated crtcs according to the need of the panels/encoders, + * adding more crtcs here if needed + */ + for (; id < num_crtcs; id++) { + + /* find a free manager for this crtc */ + for (i = 0; i < num_mgrs; i++) { + if (!channel_used(dev, i)) { + struct drm_plane *plane; + struct drm_crtc *crtc; + + plane = omap_plane_init(dev, id, true); + crtc = omap_crtc_init(dev, plane, i, id); + + BUG_ON(priv->num_crtcs >= + ARRAY_SIZE(priv->crtcs)); + + priv->crtcs[id] = crtc; + priv->num_crtcs++; + + priv->planes[id] = plane; + priv->num_planes++; + + break; + } else { + continue; + } + } + + if (i == num_mgrs) { + /* this shouldn't really happen */ + dev_err(dev->dev, "no managers left for crtc\n"); + return -ENOMEM; + } + } + + /* + * Create normal planes for the remaining overlays: + */ + for (; id < num_ovls; id++) { + struct drm_plane *plane = omap_plane_init(dev, id, false); + + BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); + priv->planes[priv->num_planes++] = plane; + } + + for (i = 0; i < priv->num_encoders; i++) { + struct drm_encoder *encoder = priv->encoders[i]; + struct omap_dss_device *dssdev = + omap_encoder_get_dssdev(encoder); + /* figure out which crtc's we can connect the encoder to: */ encoder->possible_crtcs = 0; for (id = 0; id < priv->num_crtcs; id++) { - enum omap_dss_output_id supported_outputs = - dss_feat_get_supported_outputs(pipe2chan(id)); + struct drm_crtc *crtc = priv->crtcs[id]; + enum omap_channel crtc_channel; + enum omap_dss_output_id supported_outputs; + + crtc_channel = omap_crtc_channel(crtc); + supported_outputs = + dss_feat_get_supported_outputs(crtc_channel); + if (supported_outputs & dssdev->output->id) encoder->possible_crtcs |= (1 << id); } } + DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n", + priv->num_planes, priv->num_crtcs, priv->num_encoders, + priv->num_connectors); + dev->mode_config.min_width = 32; dev->mode_config.min_height = 32; @@ -303,7 +404,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data, return ret; } -struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { +static struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH), DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH), @@ -335,10 +436,8 @@ static int dev_load(struct drm_device *dev, unsigned long flags) DBG("load: dev=%p", dev); priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev->dev, "could not allocate priv\n"); + if (!priv) return -ENOMEM; - } priv->omaprev = pdata->omaprev; @@ -569,7 +668,7 @@ static const struct dev_pm_ops omapdrm_pm_ops = { }; #endif -struct platform_driver pdev = { +static struct platform_driver pdev = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index d4f997b..215a20d 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -139,8 +139,8 @@ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); int omap_gem_resume(struct device *dev); #endif -int omap_irq_enable_vblank(struct drm_device *dev, int crtc); -void omap_irq_disable_vblank(struct drm_device *dev, int crtc); +int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id); +void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id); irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); void omap_irq_preinstall(struct drm_device *dev); int omap_irq_postinstall(struct drm_device *dev); @@ -271,39 +271,9 @@ static inline int align_pitch(int pitch, int width, int bpp) return ALIGN(pitch, 8 * bytespp); } -static inline enum omap_channel pipe2chan(int pipe) -{ - int num_mgrs = dss_feat_get_num_mgrs(); - - /* - * We usually don't want to create a CRTC for each manager, - * at least not until we have a way to expose private planes - * to userspace. Otherwise there would not be enough video - * pipes left for drm planes. The higher #'d managers tend - * to have more features so start in reverse order. - */ - return num_mgrs - pipe - 1; -} - /* map crtc to vblank mask */ -static inline uint32_t pipe2vbl(int crtc) -{ - enum omap_channel channel = pipe2chan(crtc); - return dispc_mgr_get_vsync_irq(channel); -} - -static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc) -{ - struct omap_drm_private *priv = dev->dev_private; - int i; - - for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++) - if (priv->crtcs[i] == crtc) - return i; - - BUG(); /* bogus CRTC ptr */ - return -1; -} +uint32_t pipe2vbl(struct drm_crtc *crtc); +struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder); /* should these be made into common util helpers? */ diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 7e1f2ab..c29451b 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -41,6 +41,13 @@ struct omap_encoder { struct omap_dss_device *dssdev; }; +struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + + return omap_encoder->dssdev; +} + static void omap_encoder_destroy(struct drm_encoder *encoder) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); @@ -128,13 +135,26 @@ int omap_encoder_update(struct drm_encoder *encoder, dssdev->output->manager = mgr; - ret = dssdrv->check_timings(dssdev, timings); + if (dssdrv->check_timings) { + ret = dssdrv->check_timings(dssdev, timings); + } else { + struct omap_video_timings t = {0}; + + dssdrv->get_timings(dssdev, &t); + + if (memcmp(timings, &t, sizeof(struct omap_video_timings))) + ret = -EINVAL; + else + ret = 0; + } + if (ret) { dev_err(dev->dev, "could not set timings: %d\n", ret); return ret; } - dssdrv->set_timings(dssdev, timings); + if (dssdrv->set_timings) + dssdrv->set_timings(dssdev, timings); return 0; } @@ -147,10 +167,8 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev, struct omap_encoder *omap_encoder; omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL); - if (!omap_encoder) { - dev_err(dev->dev, "could not allocate encoder\n"); + if (!omap_encoder) goto fail; - } omap_encoder->dssdev = dssdev; diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 9d5f6f6..8031402 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -418,7 +418,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); if (!omap_fb) { - dev_err(dev->dev, "could not allocate fb\n"); ret = -ENOMEM; goto fail; } diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index f0033bd..b11ce60 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -332,10 +332,8 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) int ret = 0; fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); - if (!fbdev) { - dev_err(dev->dev, "could not allocate fbdev\n"); + if (!fbdev) goto fail; - } INIT_WORK(&fbdev->work, pan_worker); diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index e8302b0..ebbdf41 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1402,10 +1402,8 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, } omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL); - if (!omap_obj) { - dev_err(dev->dev, "could not allocate GEM object\n"); + if (!omap_obj) goto fail; - } list_add(&omap_obj->mm_list, &priv->obj_list); @@ -1461,11 +1459,9 @@ void omap_gem_init(struct drm_device *dev) return; } - usergart = kzalloc(3 * sizeof(*usergart), GFP_KERNEL); - if (!usergart) { - dev_warn(dev->dev, "could not allocate usergart\n"); + usergart = kcalloc(3, sizeof(*usergart), GFP_KERNEL); + if (!usergart) return; - } /* reserve 4k aligned/wide regions for userspace mappings: */ for (i = 0; i < ARRAY_SIZE(fmts); i++) { diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index ac74d1b..0682cb5 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -178,7 +178,7 @@ out_unlock: return omap_gem_mmap_obj(obj, vma); } -struct dma_buf_ops omap_dmabuf_ops = { +static struct dma_buf_ops omap_dmabuf_ops = { .map_dma_buf = omap_gem_map_dma_buf, .unmap_dma_buf = omap_gem_unmap_dma_buf, .release = omap_gem_dmabuf_release, diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c index e4a66a3..f9eb679 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c @@ -40,7 +40,7 @@ struct page **_drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) int i, npages; /* This is the shared memory object that backs the GEM resource */ - inode = obj->filp->f_path.dentry->d_inode; + inode = file_inode(obj->filp); mapping = inode->i_mapping; npages = obj->size >> PAGE_SHIFT; diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index e01303e..9263db1 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -130,12 +130,13 @@ int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, * Zero on success, appropriate errno if the given @crtc's vblank * interrupt cannot be enabled. */ -int omap_irq_enable_vblank(struct drm_device *dev, int crtc) +int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id) { struct omap_drm_private *priv = dev->dev_private; + struct drm_crtc *crtc = priv->crtcs[crtc_id]; unsigned long flags; - DBG("dev=%p, crtc=%d", dev, crtc); + DBG("dev=%p, crtc=%d", dev, crtc_id); dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags); @@ -156,12 +157,13 @@ int omap_irq_enable_vblank(struct drm_device *dev, int crtc) * a hardware vblank counter, this routine should be a no-op, since * interrupts will have to stay on to keep the count accurate. */ -void omap_irq_disable_vblank(struct drm_device *dev, int crtc) +void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id) { struct omap_drm_private *priv = dev->dev_private; + struct drm_crtc *crtc = priv->crtcs[crtc_id]; unsigned long flags; - DBG("dev=%p, crtc=%d", dev, crtc); + DBG("dev=%p, crtc=%d", dev, crtc_id); dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags); @@ -186,9 +188,12 @@ irqreturn_t omap_irq_handler(DRM_IRQ_ARGS) VERB("irqs: %08x", irqstatus); - for (id = 0; id < priv->num_crtcs; id++) - if (irqstatus & pipe2vbl(id)) + for (id = 0; id < priv->num_crtcs; id++) { + struct drm_crtc *crtc = priv->crtcs[id]; + + if (irqstatus & pipe2vbl(crtc)) drm_handle_vblank(dev, id); + } spin_lock_irqsave(&list_lock, flags); list_for_each_entry_safe(handler, n, &priv->irq_list, node) { diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index dd68d14..8d225d7 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -247,6 +247,12 @@ static int omap_plane_update(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); omap_plane->enabled = true; + + if (plane->fb) + drm_framebuffer_unreference(plane->fb); + + drm_framebuffer_reference(fb); + return omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h, @@ -390,10 +396,8 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, DBG("%s: priv=%d", plane_names[id], private_plane); omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); - if (!omap_plane) { - dev_err(dev->dev, "could not allocate plane\n"); + if (!omap_plane) goto fail; - } ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL); if (ret) { diff --git a/drivers/gpu/drm/qxl/Kconfig b/drivers/gpu/drm/qxl/Kconfig new file mode 100644 index 0000000..2f1a57e --- /dev/null +++ b/drivers/gpu/drm/qxl/Kconfig @@ -0,0 +1,10 @@ +config DRM_QXL + tristate "QXL virtual GPU" + depends on DRM && PCI + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_TTM + help + QXL virtual GPU for Spice virtualization desktop integration. Do not enable this driver unless your distro ships a corresponding X.org QXL driver that can handle kernel modesetting. diff --git a/drivers/gpu/drm/qxl/Makefile b/drivers/gpu/drm/qxl/Makefile new file mode 100644 index 0000000..ea046ba --- /dev/null +++ b/drivers/gpu/drm/qxl/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm + +qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_fence.o qxl_release.o + +obj-$(CONFIG_DRM_QXL)+= qxl.o diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c new file mode 100644 index 0000000..736365e --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -0,0 +1,690 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +/* QXL cmd/ring handling */ + +#include "qxl_drv.h" +#include "qxl_object.h" + +static int qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap); + +struct ring { + struct qxl_ring_header header; + uint8_t elements[0]; +}; + +struct qxl_ring { + struct ring *ring; + int element_size; + int n_elements; + int prod_notify; + wait_queue_head_t *push_event; + spinlock_t lock; +}; + +void qxl_ring_free(struct qxl_ring *ring) +{ + kfree(ring); +} + +struct qxl_ring * +qxl_ring_create(struct qxl_ring_header *header, + int element_size, + int n_elements, + int prod_notify, + bool set_prod_notify, + wait_queue_head_t *push_event) +{ + struct qxl_ring *ring; + + ring = kmalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + return NULL; + + ring->ring = (struct ring *)header; + ring->element_size = element_size; + ring->n_elements = n_elements; + ring->prod_notify = prod_notify; + ring->push_event = push_event; + if (set_prod_notify) + header->notify_on_prod = ring->n_elements; + spin_lock_init(&ring->lock); + return ring; +} + +static int qxl_check_header(struct qxl_ring *ring) +{ + int ret; + struct qxl_ring_header *header = &(ring->ring->header); + unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); + ret = header->prod - header->cons < header->num_items; + if (ret == 0) + header->notify_on_cons = header->cons + 1; + spin_unlock_irqrestore(&ring->lock, flags); + return ret; +} + +static int qxl_check_idle(struct qxl_ring *ring) +{ + int ret; + struct qxl_ring_header *header = &(ring->ring->header); + unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); + ret = header->prod == header->cons; + spin_unlock_irqrestore(&ring->lock, flags); + return ret; +} + +int qxl_ring_push(struct qxl_ring *ring, + const void *new_elt, bool interruptible) +{ + struct qxl_ring_header *header = &(ring->ring->header); + uint8_t *elt; + int idx, ret; + unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); + if (header->prod - header->cons == header->num_items) { + header->notify_on_cons = header->cons + 1; + mb(); + spin_unlock_irqrestore(&ring->lock, flags); + if (!drm_can_sleep()) { + while (!qxl_check_header(ring)) + udelay(1); + } else { + if (interruptible) { + ret = wait_event_interruptible(*ring->push_event, + qxl_check_header(ring)); + if (ret) + return ret; + } else { + wait_event(*ring->push_event, + qxl_check_header(ring)); + } + + } + spin_lock_irqsave(&ring->lock, flags); + } + + idx = header->prod & (ring->n_elements - 1); + elt = ring->ring->elements + idx * ring->element_size; + + memcpy((void *)elt, new_elt, ring->element_size); + + header->prod++; + + mb(); + + if (header->prod == header->notify_on_prod) + outb(0, ring->prod_notify); + + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +static bool qxl_ring_pop(struct qxl_ring *ring, + void *element) +{ + volatile struct qxl_ring_header *header = &(ring->ring->header); + volatile uint8_t *ring_elt; + int idx; + unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); + if (header->cons == header->prod) { + header->notify_on_prod = header->cons + 1; + spin_unlock_irqrestore(&ring->lock, flags); + return false; + } + + idx = header->cons & (ring->n_elements - 1); + ring_elt = ring->ring->elements + idx * ring->element_size; + + memcpy(element, (void *)ring_elt, ring->element_size); + + header->cons++; + + spin_unlock_irqrestore(&ring->lock, flags); + return true; +} + +int +qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release, + uint32_t type, bool interruptible) +{ + struct qxl_command cmd; + + cmd.type = type; + cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset); + + return qxl_ring_push(qdev->command_ring, &cmd, interruptible); +} + +int +qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release, + uint32_t type, bool interruptible) +{ + struct qxl_command cmd; + + cmd.type = type; + cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset); + + return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible); +} + +bool qxl_queue_garbage_collect(struct qxl_device *qdev, bool flush) +{ + if (!qxl_check_idle(qdev->release_ring)) { + queue_work(qdev->gc_queue, &qdev->gc_work); + if (flush) + flush_work(&qdev->gc_work); + return true; + } + return false; +} + +int qxl_garbage_collect(struct qxl_device *qdev) +{ + struct qxl_release *release; + uint64_t id, next_id; + int i = 0; + int ret; + union qxl_release_info *info; + + while (qxl_ring_pop(qdev->release_ring, &id)) { + QXL_INFO(qdev, "popped %lld\n", id); + while (id) { + release = qxl_release_from_id_locked(qdev, id); + if (release == NULL) + break; + + ret = qxl_release_reserve(qdev, release, false); + if (ret) { + qxl_io_log(qdev, "failed to reserve release on garbage collect %lld\n", id); + DRM_ERROR("failed to reserve release %lld\n", id); + } + + info = qxl_release_map(qdev, release); + next_id = info->next; + qxl_release_unmap(qdev, release, info); + + qxl_release_unreserve(qdev, release); + QXL_INFO(qdev, "popped %lld, next %lld\n", id, + next_id); + + switch (release->type) { + case QXL_RELEASE_DRAWABLE: + case QXL_RELEASE_SURFACE_CMD: + case QXL_RELEASE_CURSOR_CMD: + break; + default: + DRM_ERROR("unexpected release type\n"); + break; + } + id = next_id; + + qxl_release_free(qdev, release); + ++i; + } + } + + QXL_INFO(qdev, "%s: %lld\n", __func__, i); + + return i; +} + +int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size, + struct qxl_bo **_bo) +{ + struct qxl_bo *bo; + int ret; + + ret = qxl_bo_create(qdev, size, false /* not kernel - device */, + QXL_GEM_DOMAIN_VRAM, NULL, &bo); + if (ret) { + DRM_ERROR("failed to allocate VRAM BO\n"); + return ret; + } + ret = qxl_bo_reserve(bo, false); + if (unlikely(ret != 0)) + goto out_unref; + + *_bo = bo; + return 0; +out_unref: + qxl_bo_unref(&bo); + return 0; +} + +static int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port) +{ + int irq_num; + long addr = qdev->io_base + port; + int ret; + + mutex_lock(&qdev->async_io_mutex); + irq_num = atomic_read(&qdev->irq_received_io_cmd); + + + if (qdev->last_sent_io_cmd > irq_num) { + ret = wait_event_interruptible(qdev->io_cmd_event, + atomic_read(&qdev->irq_received_io_cmd) > irq_num); + if (ret) + goto out; + irq_num = atomic_read(&qdev->irq_received_io_cmd); + } + outb(val, addr); + qdev->last_sent_io_cmd = irq_num + 1; + ret = wait_event_interruptible(qdev->io_cmd_event, + atomic_read(&qdev->irq_received_io_cmd) > irq_num); +out: + mutex_unlock(&qdev->async_io_mutex); + return ret; +} + +static void wait_for_io_cmd(struct qxl_device *qdev, uint8_t val, long port) +{ + int ret; + +restart: + ret = wait_for_io_cmd_user(qdev, val, port); + if (ret == -ERESTARTSYS) + goto restart; +} + +int qxl_io_update_area(struct qxl_device *qdev, struct qxl_bo *surf, + const struct qxl_rect *area) +{ + int surface_id; + uint32_t surface_width, surface_height; + int ret; + + if (!surf->hw_surf_alloc) + DRM_ERROR("got io update area with no hw surface\n"); + + if (surf->is_primary) + surface_id = 0; + else + surface_id = surf->surface_id; + surface_width = surf->surf.width; + surface_height = surf->surf.height; + + if (area->left < 0 || area->top < 0 || + area->right > surface_width || area->bottom > surface_height) { + qxl_io_log(qdev, "%s: not doing area update for " + "%d, (%d,%d,%d,%d) (%d,%d)\n", __func__, surface_id, area->left, + area->top, area->right, area->bottom, surface_width, surface_height); + return -EINVAL; + } + mutex_lock(&qdev->update_area_mutex); + qdev->ram_header->update_area = *area; + qdev->ram_header->update_surface = surface_id; + ret = wait_for_io_cmd_user(qdev, 0, QXL_IO_UPDATE_AREA_ASYNC); + mutex_unlock(&qdev->update_area_mutex); + return ret; +} + +void qxl_io_notify_oom(struct qxl_device *qdev) +{ + outb(0, qdev->io_base + QXL_IO_NOTIFY_OOM); +} + +void qxl_io_flush_release(struct qxl_device *qdev) +{ + outb(0, qdev->io_base + QXL_IO_FLUSH_RELEASE); +} + +void qxl_io_flush_surfaces(struct qxl_device *qdev) +{ + wait_for_io_cmd(qdev, 0, QXL_IO_FLUSH_SURFACES_ASYNC); +} + + +void qxl_io_destroy_primary(struct qxl_device *qdev) +{ + wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC); +} + +void qxl_io_create_primary(struct qxl_device *qdev, unsigned width, + unsigned height, unsigned offset, struct qxl_bo *bo) +{ + struct qxl_surface_create *create; + + QXL_INFO(qdev, "%s: qdev %p, ram_header %p\n", __func__, qdev, + qdev->ram_header); + create = &qdev->ram_header->create_surface; + create->format = bo->surf.format; + create->width = width; + create->height = height; + create->stride = bo->surf.stride; + create->mem = qxl_bo_physical_address(qdev, bo, offset); + + QXL_INFO(qdev, "%s: mem = %llx, from %p\n", __func__, create->mem, + bo->kptr); + + create->flags = QXL_SURF_FLAG_KEEP_DATA; + create->type = QXL_SURF_TYPE_PRIMARY; + + wait_for_io_cmd(qdev, 0, QXL_IO_CREATE_PRIMARY_ASYNC); +} + +void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id) +{ + QXL_INFO(qdev, "qxl_memslot_add %d\n", id); + wait_for_io_cmd(qdev, id, QXL_IO_MEMSLOT_ADD_ASYNC); +} + +void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(qdev->ram_header->log_buf, QXL_LOG_BUF_SIZE, fmt, args); + va_end(args); + /* + * DO not do a DRM output here - this will call printk, which will + * call back into qxl for rendering (qxl_fb) + */ + outb(0, qdev->io_base + QXL_IO_LOG); +} + +void qxl_io_reset(struct qxl_device *qdev) +{ + outb(0, qdev->io_base + QXL_IO_RESET); +} + +void qxl_io_monitors_config(struct qxl_device *qdev) +{ + qxl_io_log(qdev, "%s: %d [%dx%d+%d+%d]\n", __func__, + qdev->monitors_config ? + qdev->monitors_config->count : -1, + qdev->monitors_config && qdev->monitors_config->count ? + qdev->monitors_config->heads[0].width : -1, + qdev->monitors_config && qdev->monitors_config->count ? + qdev->monitors_config->heads[0].height : -1, + qdev->monitors_config && qdev->monitors_config->count ? + qdev->monitors_config->heads[0].x : -1, + qdev->monitors_config && qdev->monitors_config->count ? + qdev->monitors_config->heads[0].y : -1 + ); + + wait_for_io_cmd(qdev, 0, QXL_IO_MONITORS_CONFIG_ASYNC); +} + +int qxl_surface_id_alloc(struct qxl_device *qdev, + struct qxl_bo *surf) +{ + uint32_t handle = -ENOMEM; + int idr_ret; + int count = 0; +again: + if (idr_pre_get(&qdev->surf_id_idr, GFP_ATOMIC) == 0) { + DRM_ERROR("Out of memory for surf idr\n"); + kfree(surf); + goto alloc_fail; + } + + spin_lock(&qdev->surf_id_idr_lock); + idr_ret = idr_get_new_above(&qdev->surf_id_idr, NULL, 1, &handle); + spin_unlock(&qdev->surf_id_idr_lock); + + if (idr_ret == -EAGAIN) + goto again; + + if (handle >= qdev->rom->n_surfaces) { + count++; + spin_lock(&qdev->surf_id_idr_lock); + idr_remove(&qdev->surf_id_idr, handle); + spin_unlock(&qdev->surf_id_idr_lock); + qxl_reap_surface_id(qdev, 2); + goto again; + } + surf->surface_id = handle; + + spin_lock(&qdev->surf_id_idr_lock); + qdev->last_alloced_surf_id = handle; + spin_unlock(&qdev->surf_id_idr_lock); + alloc_fail: + return 0; +} + +void qxl_surface_id_dealloc(struct qxl_device *qdev, + uint32_t surface_id) +{ + spin_lock(&qdev->surf_id_idr_lock); + idr_remove(&qdev->surf_id_idr, surface_id); + spin_unlock(&qdev->surf_id_idr_lock); +} + +int qxl_hw_surface_alloc(struct qxl_device *qdev, + struct qxl_bo *surf, + struct ttm_mem_reg *new_mem) +{ + struct qxl_surface_cmd *cmd; + struct qxl_release *release; + int ret; + + if (surf->hw_surf_alloc) + return 0; + + ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_CREATE, + NULL, + &release); + if (ret) + return ret; + + cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_SURFACE_CMD_CREATE; + cmd->u.surface_create.format = surf->surf.format; + cmd->u.surface_create.width = surf->surf.width; + cmd->u.surface_create.height = surf->surf.height; + cmd->u.surface_create.stride = surf->surf.stride; + if (new_mem) { + int slot_id = surf->type == QXL_GEM_DOMAIN_VRAM ? qdev->main_mem_slot : qdev->surfaces_mem_slot; + struct qxl_memslot *slot = &(qdev->mem_slots[slot_id]); + + /* TODO - need to hold one of the locks to read tbo.offset */ + cmd->u.surface_create.data = slot->high_bits; + + cmd->u.surface_create.data |= (new_mem->start << PAGE_SHIFT) + surf->tbo.bdev->man[new_mem->mem_type].gpu_offset; + } else + cmd->u.surface_create.data = qxl_bo_physical_address(qdev, surf, 0); + cmd->surface_id = surf->surface_id; + qxl_release_unmap(qdev, release, &cmd->release_info); + + surf->surf_create = release; + + /* no need to add a release to the fence for this bo, + since it is only released when we ask to destroy the surface + and it would never signal otherwise */ + qxl_fence_releaseable(qdev, release); + + qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); + + qxl_release_unreserve(qdev, release); + + surf->hw_surf_alloc = true; + spin_lock(&qdev->surf_id_idr_lock); + idr_replace(&qdev->surf_id_idr, surf, surf->surface_id); + spin_unlock(&qdev->surf_id_idr_lock); + return 0; +} + +int qxl_hw_surface_dealloc(struct qxl_device *qdev, + struct qxl_bo *surf) +{ + struct qxl_surface_cmd *cmd; + struct qxl_release *release; + int ret; + int id; + + if (!surf->hw_surf_alloc) + return 0; + + ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_DESTROY, + surf->surf_create, + &release); + if (ret) + return ret; + + surf->surf_create = NULL; + /* remove the surface from the idr, but not the surface id yet */ + spin_lock(&qdev->surf_id_idr_lock); + idr_replace(&qdev->surf_id_idr, NULL, surf->surface_id); + spin_unlock(&qdev->surf_id_idr_lock); + surf->hw_surf_alloc = false; + + id = surf->surface_id; + surf->surface_id = 0; + + release->surface_release_id = id; + cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_SURFACE_CMD_DESTROY; + cmd->surface_id = id; + qxl_release_unmap(qdev, release, &cmd->release_info); + + qxl_fence_releaseable(qdev, release); + + qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); + + qxl_release_unreserve(qdev, release); + + + return 0; +} + +int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf) +{ + struct qxl_rect rect; + int ret; + + /* if we are evicting, we need to make sure the surface is up + to date */ + rect.left = 0; + rect.right = surf->surf.width; + rect.top = 0; + rect.bottom = surf->surf.height; +retry: + ret = qxl_io_update_area(qdev, surf, &rect); + if (ret == -ERESTARTSYS) + goto retry; + return ret; +} + +static void qxl_surface_evict_locked(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area) +{ + /* no need to update area if we are just freeing the surface normally */ + if (do_update_area) + qxl_update_surface(qdev, surf); + + /* nuke the surface id at the hw */ + qxl_hw_surface_dealloc(qdev, surf); +} + +void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area) +{ + mutex_lock(&qdev->surf_evict_mutex); + qxl_surface_evict_locked(qdev, surf, do_update_area); + mutex_unlock(&qdev->surf_evict_mutex); +} + +static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stall) +{ + int ret; + + ret = qxl_bo_reserve(surf, false); + if (ret == -EBUSY) + return -EBUSY; + + if (surf->fence.num_active_releases > 0 && stall == false) { + qxl_bo_unreserve(surf); + return -EBUSY; + } + + if (stall) + mutex_unlock(&qdev->surf_evict_mutex); + + spin_lock(&surf->tbo.bdev->fence_lock); + ret = ttm_bo_wait(&surf->tbo, true, true, !stall); + spin_unlock(&surf->tbo.bdev->fence_lock); + + if (stall) + mutex_lock(&qdev->surf_evict_mutex); + if (ret == -EBUSY) { + qxl_bo_unreserve(surf); + return -EBUSY; + } + + qxl_surface_evict_locked(qdev, surf, true); + qxl_bo_unreserve(surf); + return 0; +} + +static int qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap) +{ + int num_reaped = 0; + int i, ret; + bool stall = false; + int start = 0; + + mutex_lock(&qdev->surf_evict_mutex); +again: + + spin_lock(&qdev->surf_id_idr_lock); + start = qdev->last_alloced_surf_id + 1; + spin_unlock(&qdev->surf_id_idr_lock); + + for (i = start; i < start + qdev->rom->n_surfaces; i++) { + void *objptr; + int surfid = i % qdev->rom->n_surfaces; + + /* this avoids the case where the objects is in the + idr but has been evicted half way - its makes + the idr lookup atomic with the eviction */ + spin_lock(&qdev->surf_id_idr_lock); + objptr = idr_find(&qdev->surf_id_idr, surfid); + spin_unlock(&qdev->surf_id_idr_lock); + + if (!objptr) + continue; + + ret = qxl_reap_surf(qdev, objptr, stall); + if (ret == 0) + num_reaped++; + if (num_reaped >= max_to_reap) + break; + } + if (num_reaped == 0 && stall == false) { + stall = true; + goto again; + } + + mutex_unlock(&qdev->surf_evict_mutex); + if (num_reaped) { + usleep_range(500, 1000); + qxl_queue_garbage_collect(qdev, true); + } + + return 0; +} diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c new file mode 100644 index 0000000..c630152 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009 Red Hat <bskeggs@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* + * Authors: + * Alon Levy <alevy@redhat.com> + */ + +#include <linux/debugfs.h> + +#include "drmP.h" +#include "qxl_drv.h" +#include "qxl_object.h" + + +static int +qxl_debugfs_irq_received(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct qxl_device *qdev = node->minor->dev->dev_private; + + seq_printf(m, "%d\n", atomic_read(&qdev->irq_received)); + seq_printf(m, "%d\n", atomic_read(&qdev->irq_received_display)); + seq_printf(m, "%d\n", atomic_read(&qdev->irq_received_cursor)); + seq_printf(m, "%d\n", atomic_read(&qdev->irq_received_io_cmd)); + seq_printf(m, "%d\n", qdev->irq_received_error); + return 0; +} + +static int +qxl_debugfs_buffers_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct qxl_device *qdev = node->minor->dev->dev_private; + struct qxl_bo *bo; + + list_for_each_entry(bo, &qdev->gem.objects, list) { + seq_printf(m, "size %ld, pc %d, sync obj %p, num releases %d\n", + (unsigned long)bo->gem_base.size, bo->pin_count, + bo->tbo.sync_obj, bo->fence.num_active_releases); + } + return 0; +} + +static struct drm_info_list qxl_debugfs_list[] = { + { "irq_received", qxl_debugfs_irq_received, 0, NULL }, + { "qxl_buffers", qxl_debugfs_buffers_info, 0, NULL }, +}; +#define QXL_DEBUGFS_ENTRIES ARRAY_SIZE(qxl_debugfs_list) + +int +qxl_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(qxl_debugfs_list, QXL_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); + return 0; +} + +void +qxl_debugfs_takedown(struct drm_minor *minor) +{ + drm_debugfs_remove_files(qxl_debugfs_list, QXL_DEBUGFS_ENTRIES, + minor); +} + +int qxl_debugfs_add_files(struct qxl_device *qdev, + struct drm_info_list *files, + unsigned nfiles) +{ + unsigned i; + + for (i = 0; i < qdev->debugfs_count; i++) { + if (qdev->debugfs[i].files == files) { + /* Already registered */ + return 0; + } + } + + i = qdev->debugfs_count + 1; + if (i > QXL_DEBUGFS_MAX_COMPONENTS) { + DRM_ERROR("Reached maximum number of debugfs components.\n"); + DRM_ERROR("Report so we increase QXL_DEBUGFS_MAX_COMPONENTS.\n"); + return -EINVAL; + } + qdev->debugfs[qdev->debugfs_count].files = files; + qdev->debugfs[qdev->debugfs_count].num_files = nfiles; + qdev->debugfs_count = i; +#if defined(CONFIG_DEBUG_FS) + drm_debugfs_create_files(files, nfiles, + qdev->ddev->control->debugfs_root, + qdev->ddev->control); + drm_debugfs_create_files(files, nfiles, + qdev->ddev->primary->debugfs_root, + qdev->ddev->primary); +#endif + return 0; +} + +void qxl_debugfs_remove_files(struct qxl_device *qdev) +{ +#if defined(CONFIG_DEBUG_FS) + unsigned i; + + for (i = 0; i < qdev->debugfs_count; i++) { + drm_debugfs_remove_files(qdev->debugfs[i].files, + qdev->debugfs[i].num_files, + qdev->ddev->control); + drm_debugfs_remove_files(qdev->debugfs[i].files, + qdev->debugfs[i].num_files, + qdev->ddev->primary); + } +#endif +} diff --git a/drivers/gpu/drm/qxl/qxl_dev.h b/drivers/gpu/drm/qxl/qxl_dev.h new file mode 100644 index 0000000..94c5aec --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_dev.h @@ -0,0 +1,879 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef H_QXL_DEV +#define H_QXL_DEV + +#include <linux/types.h> + +/* + * from spice-protocol + * Release 0.10.0 + */ + +/* enums.h */ + +enum SpiceImageType { + SPICE_IMAGE_TYPE_BITMAP, + SPICE_IMAGE_TYPE_QUIC, + SPICE_IMAGE_TYPE_RESERVED, + SPICE_IMAGE_TYPE_LZ_PLT = 100, + SPICE_IMAGE_TYPE_LZ_RGB, + SPICE_IMAGE_TYPE_GLZ_RGB, + SPICE_IMAGE_TYPE_FROM_CACHE, + SPICE_IMAGE_TYPE_SURFACE, + SPICE_IMAGE_TYPE_JPEG, + SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS, + SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB, + SPICE_IMAGE_TYPE_JPEG_ALPHA, + + SPICE_IMAGE_TYPE_ENUM_END +}; + +enum SpiceBitmapFmt { + SPICE_BITMAP_FMT_INVALID, + SPICE_BITMAP_FMT_1BIT_LE, + SPICE_BITMAP_FMT_1BIT_BE, + SPICE_BITMAP_FMT_4BIT_LE, + SPICE_BITMAP_FMT_4BIT_BE, + SPICE_BITMAP_FMT_8BIT, + SPICE_BITMAP_FMT_16BIT, + SPICE_BITMAP_FMT_24BIT, + SPICE_BITMAP_FMT_32BIT, + SPICE_BITMAP_FMT_RGBA, + + SPICE_BITMAP_FMT_ENUM_END +}; + +enum SpiceSurfaceFmt { + SPICE_SURFACE_FMT_INVALID, + SPICE_SURFACE_FMT_1_A, + SPICE_SURFACE_FMT_8_A = 8, + SPICE_SURFACE_FMT_16_555 = 16, + SPICE_SURFACE_FMT_32_xRGB = 32, + SPICE_SURFACE_FMT_16_565 = 80, + SPICE_SURFACE_FMT_32_ARGB = 96, + + SPICE_SURFACE_FMT_ENUM_END +}; + +enum SpiceClipType { + SPICE_CLIP_TYPE_NONE, + SPICE_CLIP_TYPE_RECTS, + + SPICE_CLIP_TYPE_ENUM_END +}; + +enum SpiceRopd { + SPICE_ROPD_INVERS_SRC = (1 << 0), + SPICE_ROPD_INVERS_BRUSH = (1 << 1), + SPICE_ROPD_INVERS_DEST = (1 << 2), + SPICE_ROPD_OP_PUT = (1 << 3), + SPICE_ROPD_OP_OR = (1 << 4), + SPICE_ROPD_OP_AND = (1 << 5), + SPICE_ROPD_OP_XOR = (1 << 6), + SPICE_ROPD_OP_BLACKNESS = (1 << 7), + SPICE_ROPD_OP_WHITENESS = (1 << 8), + SPICE_ROPD_OP_INVERS = (1 << 9), + SPICE_ROPD_INVERS_RES = (1 << 10), + + SPICE_ROPD_MASK = 0x7ff +}; + +enum SpiceBrushType { + SPICE_BRUSH_TYPE_NONE, + SPICE_BRUSH_TYPE_SOLID, + SPICE_BRUSH_TYPE_PATTERN, + + SPICE_BRUSH_TYPE_ENUM_END +}; + +enum SpiceCursorType { + SPICE_CURSOR_TYPE_ALPHA, + SPICE_CURSOR_TYPE_MONO, + SPICE_CURSOR_TYPE_COLOR4, + SPICE_CURSOR_TYPE_COLOR8, + SPICE_CURSOR_TYPE_COLOR16, + SPICE_CURSOR_TYPE_COLOR24, + SPICE_CURSOR_TYPE_COLOR32, + + SPICE_CURSOR_TYPE_ENUM_END +}; + +/* qxl_dev.h */ + +#pragma pack(push, 1) + +#define REDHAT_PCI_VENDOR_ID 0x1b36 + +/* 0x100-0x11f reserved for spice, 0x1ff used for unstable work */ +#define QXL_DEVICE_ID_STABLE 0x0100 + +enum { + QXL_REVISION_STABLE_V04 = 0x01, + QXL_REVISION_STABLE_V06 = 0x02, + QXL_REVISION_STABLE_V10 = 0x03, + QXL_REVISION_STABLE_V12 = 0x04, +}; + +#define QXL_DEVICE_ID_DEVEL 0x01ff +#define QXL_REVISION_DEVEL 0x01 + +#define QXL_ROM_MAGIC (*(uint32_t *)"QXRO") +#define QXL_RAM_MAGIC (*(uint32_t *)"QXRA") + +enum { + QXL_RAM_RANGE_INDEX, + QXL_VRAM_RANGE_INDEX, + QXL_ROM_RANGE_INDEX, + QXL_IO_RANGE_INDEX, + + QXL_PCI_RANGES +}; + +/* qxl-1 compat: append only */ +enum { + QXL_IO_NOTIFY_CMD, + QXL_IO_NOTIFY_CURSOR, + QXL_IO_UPDATE_AREA, + QXL_IO_UPDATE_IRQ, + QXL_IO_NOTIFY_OOM, + QXL_IO_RESET, + QXL_IO_SET_MODE, /* qxl-1 */ + QXL_IO_LOG, + /* appended for qxl-2 */ + QXL_IO_MEMSLOT_ADD, + QXL_IO_MEMSLOT_DEL, + QXL_IO_DETACH_PRIMARY, + QXL_IO_ATTACH_PRIMARY, + QXL_IO_CREATE_PRIMARY, + QXL_IO_DESTROY_PRIMARY, + QXL_IO_DESTROY_SURFACE_WAIT, + QXL_IO_DESTROY_ALL_SURFACES, + /* appended for qxl-3 */ + QXL_IO_UPDATE_AREA_ASYNC, + QXL_IO_MEMSLOT_ADD_ASYNC, + QXL_IO_CREATE_PRIMARY_ASYNC, + QXL_IO_DESTROY_PRIMARY_ASYNC, + QXL_IO_DESTROY_SURFACE_ASYNC, + QXL_IO_DESTROY_ALL_SURFACES_ASYNC, + QXL_IO_FLUSH_SURFACES_ASYNC, + QXL_IO_FLUSH_RELEASE, + /* appended for qxl-4 */ + QXL_IO_MONITORS_CONFIG_ASYNC, + + QXL_IO_RANGE_SIZE +}; + +typedef uint64_t QXLPHYSICAL; +typedef int32_t QXLFIXED; /* fixed 28.4 */ + +struct qxl_point_fix { + QXLFIXED x; + QXLFIXED y; +}; + +struct qxl_point { + int32_t x; + int32_t y; +}; + +struct qxl_point_1_6 { + int16_t x; + int16_t y; +}; + +struct qxl_rect { + int32_t top; + int32_t left; + int32_t bottom; + int32_t right; +}; + +struct qxl_urect { + uint32_t top; + uint32_t left; + uint32_t bottom; + uint32_t right; +}; + +/* qxl-1 compat: append only */ +struct qxl_rom { + uint32_t magic; + uint32_t id; + uint32_t update_id; + uint32_t compression_level; + uint32_t log_level; + uint32_t mode; /* qxl-1 */ + uint32_t modes_offset; + uint32_t num_io_pages; + uint32_t pages_offset; /* qxl-1 */ + uint32_t draw_area_offset; /* qxl-1 */ + uint32_t surface0_area_size; /* qxl-1 name: draw_area_size */ + uint32_t ram_header_offset; + uint32_t mm_clock; + /* appended for qxl-2 */ + uint32_t n_surfaces; + uint64_t flags; + uint8_t slots_start; + uint8_t slots_end; + uint8_t slot_gen_bits; + uint8_t slot_id_bits; + uint8_t slot_generation; + /* appended for qxl-4 */ + uint8_t client_present; + uint8_t client_capabilities[58]; + uint32_t client_monitors_config_crc; + struct { + uint16_t count; + uint16_t padding; + struct qxl_urect heads[64]; + } client_monitors_config; +}; + +/* qxl-1 compat: fixed */ +struct qxl_mode { + uint32_t id; + uint32_t x_res; + uint32_t y_res; + uint32_t bits; + uint32_t stride; + uint32_t x_mili; + uint32_t y_mili; + uint32_t orientation; +}; + +/* qxl-1 compat: fixed */ +struct qxl_modes { + uint32_t n_modes; + struct qxl_mode modes[0]; +}; + +/* qxl-1 compat: append only */ +enum qxl_cmd_type { + QXL_CMD_NOP, + QXL_CMD_DRAW, + QXL_CMD_UPDATE, + QXL_CMD_CURSOR, + QXL_CMD_MESSAGE, + QXL_CMD_SURFACE, +}; + +/* qxl-1 compat: fixed */ +struct qxl_command { + QXLPHYSICAL data; + uint32_t type; + uint32_t padding; +}; + +#define QXL_COMMAND_FLAG_COMPAT (1<<0) +#define QXL_COMMAND_FLAG_COMPAT_16BPP (2<<0) + +struct qxl_command_ext { + struct qxl_command cmd; + uint32_t group_id; + uint32_t flags; +}; + +struct qxl_mem_slot { + uint64_t mem_start; + uint64_t mem_end; +}; + +#define QXL_SURF_TYPE_PRIMARY 0 + +#define QXL_SURF_FLAG_KEEP_DATA (1 << 0) + +struct qxl_surface_create { + uint32_t width; + uint32_t height; + int32_t stride; + uint32_t format; + uint32_t position; + uint32_t mouse_mode; + uint32_t flags; + uint32_t type; + QXLPHYSICAL mem; +}; + +#define QXL_COMMAND_RING_SIZE 32 +#define QXL_CURSOR_RING_SIZE 32 +#define QXL_RELEASE_RING_SIZE 8 + +#define QXL_LOG_BUF_SIZE 4096 + +#define QXL_INTERRUPT_DISPLAY (1 << 0) +#define QXL_INTERRUPT_CURSOR (1 << 1) +#define QXL_INTERRUPT_IO_CMD (1 << 2) +#define QXL_INTERRUPT_ERROR (1 << 3) +#define QXL_INTERRUPT_CLIENT (1 << 4) +#define QXL_INTERRUPT_CLIENT_MONITORS_CONFIG (1 << 5) + +struct qxl_ring_header { + uint32_t num_items; + uint32_t prod; + uint32_t notify_on_prod; + uint32_t cons; + uint32_t notify_on_cons; +}; + +/* qxl-1 compat: append only */ +struct qxl_ram_header { + uint32_t magic; + uint32_t int_pending; + uint32_t int_mask; + uint8_t log_buf[QXL_LOG_BUF_SIZE]; + struct qxl_ring_header cmd_ring_hdr; + struct qxl_command cmd_ring[QXL_COMMAND_RING_SIZE]; + struct qxl_ring_header cursor_ring_hdr; + struct qxl_command cursor_ring[QXL_CURSOR_RING_SIZE]; + struct qxl_ring_header release_ring_hdr; + uint64_t release_ring[QXL_RELEASE_RING_SIZE]; + struct qxl_rect update_area; + /* appended for qxl-2 */ + uint32_t update_surface; + struct qxl_mem_slot mem_slot; + struct qxl_surface_create create_surface; + uint64_t flags; + + /* appended for qxl-4 */ + + /* used by QXL_IO_MONITORS_CONFIG_ASYNC */ + QXLPHYSICAL monitors_config; + uint8_t guest_capabilities[64]; +}; + +union qxl_release_info { + uint64_t id; /* in */ + uint64_t next; /* out */ +}; + +struct qxl_release_info_ext { + union qxl_release_info *info; + uint32_t group_id; +}; + +struct qxl_data_chunk { + uint32_t data_size; + QXLPHYSICAL prev_chunk; + QXLPHYSICAL next_chunk; + uint8_t data[0]; +}; + +struct qxl_message { + union qxl_release_info release_info; + uint8_t data[0]; +}; + +struct qxl_compat_update_cmd { + union qxl_release_info release_info; + struct qxl_rect area; + uint32_t update_id; +}; + +struct qxl_update_cmd { + union qxl_release_info release_info; + struct qxl_rect area; + uint32_t update_id; + uint32_t surface_id; +}; + +struct qxl_cursor_header { + uint64_t unique; + uint16_t type; + uint16_t width; + uint16_t height; + uint16_t hot_spot_x; + uint16_t hot_spot_y; +}; + +struct qxl_cursor { + struct qxl_cursor_header header; + uint32_t data_size; + struct qxl_data_chunk chunk; +}; + +enum { + QXL_CURSOR_SET, + QXL_CURSOR_MOVE, + QXL_CURSOR_HIDE, + QXL_CURSOR_TRAIL, +}; + +#define QXL_CURSOR_DEVICE_DATA_SIZE 128 + +struct qxl_cursor_cmd { + union qxl_release_info release_info; + uint8_t type; + union { + struct { + struct qxl_point_1_6 position; + uint8_t visible; + QXLPHYSICAL shape; + } set; + struct { + uint16_t length; + uint16_t frequency; + } trail; + struct qxl_point_1_6 position; + } u; + /* todo: dynamic size from rom */ + uint8_t device_data[QXL_CURSOR_DEVICE_DATA_SIZE]; +}; + +enum { + QXL_DRAW_NOP, + QXL_DRAW_FILL, + QXL_DRAW_OPAQUE, + QXL_DRAW_COPY, + QXL_COPY_BITS, + QXL_DRAW_BLEND, + QXL_DRAW_BLACKNESS, + QXL_DRAW_WHITENESS, + QXL_DRAW_INVERS, + QXL_DRAW_ROP3, + QXL_DRAW_STROKE, + QXL_DRAW_TEXT, + QXL_DRAW_TRANSPARENT, + QXL_DRAW_ALPHA_BLEND, + QXL_DRAW_COMPOSITE +}; + +struct qxl_raster_glyph { + struct qxl_point render_pos; + struct qxl_point glyph_origin; + uint16_t width; + uint16_t height; + uint8_t data[0]; +}; + +struct qxl_string { + uint32_t data_size; + uint16_t length; + uint16_t flags; + struct qxl_data_chunk chunk; +}; + +struct qxl_copy_bits { + struct qxl_point src_pos; +}; + +enum qxl_effect_type { + QXL_EFFECT_BLEND = 0, + QXL_EFFECT_OPAQUE = 1, + QXL_EFFECT_REVERT_ON_DUP = 2, + QXL_EFFECT_BLACKNESS_ON_DUP = 3, + QXL_EFFECT_WHITENESS_ON_DUP = 4, + QXL_EFFECT_NOP_ON_DUP = 5, + QXL_EFFECT_NOP = 6, + QXL_EFFECT_OPAQUE_BRUSH = 7 +}; + +struct qxl_pattern { + QXLPHYSICAL pat; + struct qxl_point pos; +}; + +struct qxl_brush { + uint32_t type; + union { + uint32_t color; + struct qxl_pattern pattern; + } u; +}; + +struct qxl_q_mask { + uint8_t flags; + struct qxl_point pos; + QXLPHYSICAL bitmap; +}; + +struct qxl_fill { + struct qxl_brush brush; + uint16_t rop_descriptor; + struct qxl_q_mask mask; +}; + +struct qxl_opaque { + QXLPHYSICAL src_bitmap; + struct qxl_rect src_area; + struct qxl_brush brush; + uint16_t rop_descriptor; + uint8_t scale_mode; + struct qxl_q_mask mask; +}; + +struct qxl_copy { + QXLPHYSICAL src_bitmap; + struct qxl_rect src_area; + uint16_t rop_descriptor; + uint8_t scale_mode; + struct qxl_q_mask mask; +}; + +struct qxl_transparent { + QXLPHYSICAL src_bitmap; + struct qxl_rect src_area; + uint32_t src_color; + uint32_t true_color; +}; + +struct qxl_alpha_blend { + uint16_t alpha_flags; + uint8_t alpha; + QXLPHYSICAL src_bitmap; + struct qxl_rect src_area; +}; + +struct qxl_compat_alpha_blend { + uint8_t alpha; + QXLPHYSICAL src_bitmap; + struct qxl_rect src_area; +}; + +struct qxl_rop_3 { + QXLPHYSICAL src_bitmap; + struct qxl_rect src_area; + struct qxl_brush brush; + uint8_t rop3; + uint8_t scale_mode; + struct qxl_q_mask mask; +}; + +struct qxl_line_attr { + uint8_t flags; + uint8_t join_style; + uint8_t end_style; + uint8_t style_nseg; + QXLFIXED width; + QXLFIXED miter_limit; + QXLPHYSICAL style; +}; + +struct qxl_stroke { + QXLPHYSICAL path; + struct qxl_line_attr attr; + struct qxl_brush brush; + uint16_t fore_mode; + uint16_t back_mode; +}; + +struct qxl_text { + QXLPHYSICAL str; + struct qxl_rect back_area; + struct qxl_brush fore_brush; + struct qxl_brush back_brush; + uint16_t fore_mode; + uint16_t back_mode; +}; + +struct qxl_mask { + struct qxl_q_mask mask; +}; + +struct qxl_clip { + uint32_t type; + QXLPHYSICAL data; +}; + +enum qxl_operator { + QXL_OP_CLEAR = 0x00, + QXL_OP_SOURCE = 0x01, + QXL_OP_DST = 0x02, + QXL_OP_OVER = 0x03, + QXL_OP_OVER_REVERSE = 0x04, + QXL_OP_IN = 0x05, + QXL_OP_IN_REVERSE = 0x06, + QXL_OP_OUT = 0x07, + QXL_OP_OUT_REVERSE = 0x08, + QXL_OP_ATOP = 0x09, + QXL_OP_ATOP_REVERSE = 0x0a, + QXL_OP_XOR = 0x0b, + QXL_OP_ADD = 0x0c, + QXL_OP_SATURATE = 0x0d, + /* Note the jump here from 0x0d to 0x30 */ + QXL_OP_MULTIPLY = 0x30, + QXL_OP_SCREEN = 0x31, + QXL_OP_OVERLAY = 0x32, + QXL_OP_DARKEN = 0x33, + QXL_OP_LIGHTEN = 0x34, + QXL_OP_COLOR_DODGE = 0x35, + QXL_OP_COLOR_BURN = 0x36, + QXL_OP_HARD_LIGHT = 0x37, + QXL_OP_SOFT_LIGHT = 0x38, + QXL_OP_DIFFERENCE = 0x39, + QXL_OP_EXCLUSION = 0x3a, + QXL_OP_HSL_HUE = 0x3b, + QXL_OP_HSL_SATURATION = 0x3c, + QXL_OP_HSL_COLOR = 0x3d, + QXL_OP_HSL_LUMINOSITY = 0x3e +}; + +struct qxl_transform { + uint32_t t00; + uint32_t t01; + uint32_t t02; + uint32_t t10; + uint32_t t11; + uint32_t t12; +}; + +/* The flags field has the following bit fields: + * + * operator: [ 0 - 7 ] + * src_filter: [ 8 - 10 ] + * mask_filter: [ 11 - 13 ] + * src_repeat: [ 14 - 15 ] + * mask_repeat: [ 16 - 17 ] + * component_alpha: [ 18 - 18 ] + * reserved: [ 19 - 31 ] + * + * The repeat and filter values are those of pixman: + * REPEAT_NONE = 0 + * REPEAT_NORMAL = 1 + * REPEAT_PAD = 2 + * REPEAT_REFLECT = 3 + * + * The filter values are: + * FILTER_NEAREST = 0 + * FILTER_BILINEAR = 1 + */ +struct qxl_composite { + uint32_t flags; + + QXLPHYSICAL src; + QXLPHYSICAL src_transform; /* May be NULL */ + QXLPHYSICAL mask; /* May be NULL */ + QXLPHYSICAL mask_transform; /* May be NULL */ + struct qxl_point_1_6 src_origin; + struct qxl_point_1_6 mask_origin; +}; + +struct qxl_compat_drawable { + union qxl_release_info release_info; + uint8_t effect; + uint8_t type; + uint16_t bitmap_offset; + struct qxl_rect bitmap_area; + struct qxl_rect bbox; + struct qxl_clip clip; + uint32_t mm_time; + union { + struct qxl_fill fill; + struct qxl_opaque opaque; + struct qxl_copy copy; + struct qxl_transparent transparent; + struct qxl_compat_alpha_blend alpha_blend; + struct qxl_copy_bits copy_bits; + struct qxl_copy blend; + struct qxl_rop_3 rop3; + struct qxl_stroke stroke; + struct qxl_text text; + struct qxl_mask blackness; + struct qxl_mask invers; + struct qxl_mask whiteness; + } u; +}; + +struct qxl_drawable { + union qxl_release_info release_info; + uint32_t surface_id; + uint8_t effect; + uint8_t type; + uint8_t self_bitmap; + struct qxl_rect self_bitmap_area; + struct qxl_rect bbox; + struct qxl_clip clip; + uint32_t mm_time; + int32_t surfaces_dest[3]; + struct qxl_rect surfaces_rects[3]; + union { + struct qxl_fill fill; + struct qxl_opaque opaque; + struct qxl_copy copy; + struct qxl_transparent transparent; + struct qxl_alpha_blend alpha_blend; + struct qxl_copy_bits copy_bits; + struct qxl_copy blend; + struct qxl_rop_3 rop3; + struct qxl_stroke stroke; + struct qxl_text text; + struct qxl_mask blackness; + struct qxl_mask invers; + struct qxl_mask whiteness; + struct qxl_composite composite; + } u; +}; + +enum qxl_surface_cmd_type { + QXL_SURFACE_CMD_CREATE, + QXL_SURFACE_CMD_DESTROY, +}; + +struct qxl_surface { + uint32_t format; + uint32_t width; + uint32_t height; + int32_t stride; + QXLPHYSICAL data; +}; + +struct qxl_surface_cmd { + union qxl_release_info release_info; + uint32_t surface_id; + uint8_t type; + uint32_t flags; + union { + struct qxl_surface surface_create; + } u; +}; + +struct qxl_clip_rects { + uint32_t num_rects; + struct qxl_data_chunk chunk; +}; + +enum { + QXL_PATH_BEGIN = (1 << 0), + QXL_PATH_END = (1 << 1), + QXL_PATH_CLOSE = (1 << 3), + QXL_PATH_BEZIER = (1 << 4), +}; + +struct qxl_path_seg { + uint32_t flags; + uint32_t count; + struct qxl_point_fix points[0]; +}; + +struct qxl_path { + uint32_t data_size; + struct qxl_data_chunk chunk; +}; + +enum { + QXL_IMAGE_GROUP_DRIVER, + QXL_IMAGE_GROUP_DEVICE, + QXL_IMAGE_GROUP_RED, + QXL_IMAGE_GROUP_DRIVER_DONT_CACHE, +}; + +struct qxl_image_id { + uint32_t group; + uint32_t unique; +}; + +union qxl_image_id_union { + struct qxl_image_id id; + uint64_t value; +}; + +enum qxl_image_flags { + QXL_IMAGE_CACHE = (1 << 0), + QXL_IMAGE_HIGH_BITS_SET = (1 << 1), +}; + +enum qxl_bitmap_flags { + QXL_BITMAP_DIRECT = (1 << 0), + QXL_BITMAP_UNSTABLE = (1 << 1), + QXL_BITMAP_TOP_DOWN = (1 << 2), /* == SPICE_BITMAP_FLAGS_TOP_DOWN */ +}; + +#define QXL_SET_IMAGE_ID(image, _group, _unique) { \ + (image)->descriptor.id = (((uint64_t)_unique) << 32) | _group; \ +} + +struct qxl_image_descriptor { + uint64_t id; + uint8_t type; + uint8_t flags; + uint32_t width; + uint32_t height; +}; + +struct qxl_palette { + uint64_t unique; + uint16_t num_ents; + uint32_t ents[0]; +}; + +struct qxl_bitmap { + uint8_t format; + uint8_t flags; + uint32_t x; + uint32_t y; + uint32_t stride; + QXLPHYSICAL palette; + QXLPHYSICAL data; /* data[0] ? */ +}; + +struct qxl_surface_id { + uint32_t surface_id; +}; + +struct qxl_encoder_data { + uint32_t data_size; + uint8_t data[0]; +}; + +struct qxl_image { + struct qxl_image_descriptor descriptor; + union { /* variable length */ + struct qxl_bitmap bitmap; + struct qxl_encoder_data quic; + struct qxl_surface_id surface_image; + } u; +}; + +/* A QXLHead is a single monitor output backed by a QXLSurface. + * x and y offsets are unsigned since they are used in relation to + * the given surface, not the same as the x, y coordinates in the guest + * screen reference frame. */ +struct qxl_head { + uint32_t id; + uint32_t surface_id; + uint32_t width; + uint32_t height; + uint32_t x; + uint32_t y; + uint32_t flags; +}; + +struct qxl_monitors_config { + uint16_t count; + uint16_t max_allowed; /* If it is 0 no fixed limit is given by the + driver */ + struct qxl_head heads[0]; +}; + +#pragma pack(pop) + +#endif /* _H_QXL_DEV */ diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c new file mode 100644 index 0000000..fcfd443 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -0,0 +1,982 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + + +#include "linux/crc32.h" + +#include "qxl_drv.h" +#include "qxl_object.h" +#include "drm_crtc_helper.h" + +static void qxl_crtc_set_to_mode(struct qxl_device *qdev, + struct drm_connector *connector, + struct qxl_head *head) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *t; + int width = head->width; + int height = head->height; + + if (width < 320 || height < 240) { + qxl_io_log(qdev, "%s: bad head: %dx%d", width, height); + width = 1024; + height = 768; + } + if (width * height * 4 > 16*1024*1024) { + width = 1024; + height = 768; + } + /* TODO: go over regular modes and removed preferred? */ + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) + drm_mode_remove(connector, mode); + mode = drm_cvt_mode(dev, width, height, 60, false, false, false); + mode->type |= DRM_MODE_TYPE_PREFERRED; + mode->status = MODE_OK; + drm_mode_probed_add(connector, mode); + qxl_io_log(qdev, "%s: %d x %d\n", __func__, width, height); +} + +void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev) +{ + struct drm_connector *connector; + int i; + struct drm_device *dev = qdev->ddev; + + i = 0; + qxl_io_log(qdev, "%s: %d, %d\n", __func__, + dev->mode_config.num_connector, + qdev->monitors_config->count); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (i > qdev->monitors_config->count) { + /* crtc will be reported as disabled */ + continue; + } + qxl_crtc_set_to_mode(qdev, connector, + &qdev->monitors_config->heads[i]); + ++i; + } +} + +void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count) +{ + if (qdev->client_monitors_config && + count > qdev->client_monitors_config->count) { + kfree(qdev->client_monitors_config); + qdev->client_monitors_config = NULL; + } + if (!qdev->client_monitors_config) { + qdev->client_monitors_config = kzalloc( + sizeof(struct qxl_monitors_config) + + sizeof(struct qxl_head) * count, GFP_KERNEL); + if (!qdev->client_monitors_config) { + qxl_io_log(qdev, + "%s: allocation failure for %u heads\n", + __func__, count); + return; + } + } + qdev->client_monitors_config->count = count; +} + +static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) +{ + int i; + int num_monitors; + uint32_t crc; + + BUG_ON(!qdev->monitors_config); + num_monitors = qdev->rom->client_monitors_config.count; + crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config, + sizeof(qdev->rom->client_monitors_config)); + if (crc != qdev->rom->client_monitors_config_crc) { + qxl_io_log(qdev, "crc mismatch: have %X (%d) != %X\n", crc, + sizeof(qdev->rom->client_monitors_config), + qdev->rom->client_monitors_config_crc); + return 1; + } + if (num_monitors > qdev->monitors_config->max_allowed) { + DRM_INFO("client monitors list will be truncated: %d < %d\n", + qdev->monitors_config->max_allowed, num_monitors); + num_monitors = qdev->monitors_config->max_allowed; + } else { + num_monitors = qdev->rom->client_monitors_config.count; + } + qxl_alloc_client_monitors_config(qdev, num_monitors); + /* we copy max from the client but it isn't used */ + qdev->client_monitors_config->max_allowed = + qdev->monitors_config->max_allowed; + for (i = 0 ; i < qdev->client_monitors_config->count ; ++i) { + struct qxl_urect *c_rect = + &qdev->rom->client_monitors_config.heads[i]; + struct qxl_head *client_head = + &qdev->client_monitors_config->heads[i]; + struct qxl_head *head = &qdev->monitors_config->heads[i]; + client_head->x = head->x = c_rect->left; + client_head->y = head->y = c_rect->top; + client_head->width = head->width = + c_rect->right - c_rect->left; + client_head->height = head->height = + c_rect->bottom - c_rect->top; + client_head->surface_id = head->surface_id = 0; + client_head->id = head->id = i; + client_head->flags = head->flags = 0; + QXL_DEBUG(qdev, "read %dx%d+%d+%d\n", head->width, head->height, + head->x, head->y); + } + return 0; +} + +void qxl_display_read_client_monitors_config(struct qxl_device *qdev) +{ + + while (qxl_display_copy_rom_client_monitors_config(qdev)) { + qxl_io_log(qdev, "failed crc check for client_monitors_config," + " retrying\n"); + } + qxl_crtc_set_from_monitors_config(qdev); + /* fire off a uevent and let userspace tell us what to do */ + qxl_io_log(qdev, "calling drm_sysfs_hotplug_event\n"); + drm_sysfs_hotplug_event(qdev->ddev); +} + +static int qxl_add_monitors_config_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct qxl_device *qdev = dev->dev_private; + struct qxl_output *output = drm_connector_to_qxl_output(connector); + int h = output->index; + struct drm_display_mode *mode = NULL; + struct qxl_head *head; + + if (!qdev->monitors_config) + return 0; + head = &qdev->monitors_config->heads[h]; + + mode = drm_cvt_mode(dev, head->width, head->height, 60, false, false, + false); + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + return 1; +} + +static int qxl_add_common_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode = NULL; + int i; + struct mode_size { + int w; + int h; + } common_modes[] = { + { 640, 480}, + { 720, 480}, + { 800, 600}, + { 848, 480}, + {1024, 768}, + {1152, 768}, + {1280, 720}, + {1280, 800}, + {1280, 854}, + {1280, 960}, + {1280, 1024}, + {1440, 900}, + {1400, 1050}, + {1680, 1050}, + {1600, 1200}, + {1920, 1080}, + {1920, 1200} + }; + + for (i = 0; i < ARRAY_SIZE(common_modes); i++) { + if (common_modes[i].w < 320 || common_modes[i].h < 200) + continue; + + mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, + 60, false, false, false); + if (common_modes[i].w == 1024 && common_modes[i].h == 768) + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + } + return i - 1; +} + +static void qxl_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t start, uint32_t size) +{ + /* TODO */ +} + +static void qxl_crtc_destroy(struct drm_crtc *crtc) +{ + struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(qxl_crtc); +} + +static void +qxl_hide_cursor(struct qxl_device *qdev) +{ + struct qxl_release *release; + struct qxl_cursor_cmd *cmd; + int ret; + + ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD, + &release, NULL); + + cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_CURSOR_HIDE; + qxl_release_unmap(qdev, release, &cmd->release_info); + + qxl_fence_releaseable(qdev, release); + qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); + qxl_release_unreserve(qdev, release); +} + +static int qxl_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); + struct drm_gem_object *obj; + struct qxl_cursor *cursor; + struct qxl_cursor_cmd *cmd; + struct qxl_bo *cursor_bo, *user_bo; + struct qxl_release *release; + void *user_ptr; + + int size = 64*64*4; + int ret = 0; + if (!handle) { + qxl_hide_cursor(qdev); + return 0; + } + + obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); + if (!obj) { + DRM_ERROR("cannot find cursor object\n"); + return -ENOENT; + } + + user_bo = gem_to_qxl_bo(obj); + + ret = qxl_bo_reserve(user_bo, false); + if (ret) + goto out_unref; + + ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL); + if (ret) + goto out_unreserve; + + ret = qxl_bo_kmap(user_bo, &user_ptr); + if (ret) + goto out_unpin; + + ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), + QXL_RELEASE_CURSOR_CMD, + &release, NULL); + if (ret) + goto out_kunmap; + ret = qxl_alloc_bo_reserved(qdev, sizeof(struct qxl_cursor) + size, + &cursor_bo); + if (ret) + goto out_free_release; + ret = qxl_bo_kmap(cursor_bo, (void **)&cursor); + if (ret) + goto out_free_bo; + + cursor->header.unique = 0; + cursor->header.type = SPICE_CURSOR_TYPE_ALPHA; + cursor->header.width = 64; + cursor->header.height = 64; + cursor->header.hot_spot_x = 0; + cursor->header.hot_spot_y = 0; + cursor->data_size = size; + cursor->chunk.next_chunk = 0; + cursor->chunk.prev_chunk = 0; + cursor->chunk.data_size = size; + + memcpy(cursor->chunk.data, user_ptr, size); + + qxl_bo_kunmap(cursor_bo); + + /* finish with the userspace bo */ + qxl_bo_kunmap(user_bo); + qxl_bo_unpin(user_bo); + qxl_bo_unreserve(user_bo); + drm_gem_object_unreference_unlocked(obj); + + cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_CURSOR_SET; + cmd->u.set.position.x = qcrtc->cur_x; + cmd->u.set.position.y = qcrtc->cur_y; + + cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0); + qxl_release_add_res(qdev, release, cursor_bo); + + cmd->u.set.visible = 1; + qxl_release_unmap(qdev, release, &cmd->release_info); + + qxl_fence_releaseable(qdev, release); + qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); + qxl_release_unreserve(qdev, release); + + qxl_bo_unreserve(cursor_bo); + qxl_bo_unref(&cursor_bo); + + return ret; +out_free_bo: + qxl_bo_unref(&cursor_bo); +out_free_release: + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); +out_kunmap: + qxl_bo_kunmap(user_bo); +out_unpin: + qxl_bo_unpin(user_bo); +out_unreserve: + qxl_bo_unreserve(user_bo); +out_unref: + drm_gem_object_unreference_unlocked(obj); + return ret; +} + +static int qxl_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); + struct qxl_release *release; + struct qxl_cursor_cmd *cmd; + int ret; + + ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD, + &release, NULL); + + qcrtc->cur_x = x; + qcrtc->cur_y = y; + + cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_CURSOR_MOVE; + cmd->u.position.x = qcrtc->cur_x; + cmd->u.position.y = qcrtc->cur_y; + qxl_release_unmap(qdev, release, &cmd->release_info); + + qxl_fence_releaseable(qdev, release); + qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); + qxl_release_unreserve(qdev, release); + return 0; +} + + +static const struct drm_crtc_funcs qxl_crtc_funcs = { + .cursor_set = qxl_crtc_cursor_set, + .cursor_move = qxl_crtc_cursor_move, + .gamma_set = qxl_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = qxl_crtc_destroy, +}; + +static void qxl_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct qxl_framebuffer *qxl_fb = to_qxl_framebuffer(fb); + + if (qxl_fb->obj) + drm_gem_object_unreference_unlocked(qxl_fb->obj); + drm_framebuffer_cleanup(fb); + kfree(qxl_fb); +} + +static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + /* TODO: vmwgfx where this was cribbed from had locking. Why? */ + struct qxl_framebuffer *qxl_fb = to_qxl_framebuffer(fb); + struct qxl_device *qdev = qxl_fb->base.dev->dev_private; + struct drm_clip_rect norect; + struct qxl_bo *qobj; + int inc = 1; + + qobj = gem_to_qxl_bo(qxl_fb->obj); + if (qxl_fb != qdev->active_user_framebuffer) { + DRM_INFO("%s: qxl_fb 0x%p != qdev->active_user_framebuffer 0x%p\n", + __func__, qxl_fb, qdev->active_user_framebuffer); + } + if (!num_clips) { + num_clips = 1; + clips = &norect; + norect.x1 = norect.y1 = 0; + norect.x2 = fb->width; + norect.y2 = fb->height; + } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { + num_clips /= 2; + inc = 2; /* skip source rects */ + } + + qxl_draw_dirty_fb(qdev, qxl_fb, qobj, flags, color, + clips, num_clips, inc); + return 0; +} + +static const struct drm_framebuffer_funcs qxl_fb_funcs = { + .destroy = qxl_user_framebuffer_destroy, + .dirty = qxl_framebuffer_surface_dirty, +/* TODO? + * .create_handle = qxl_user_framebuffer_create_handle, */ +}; + +int +qxl_framebuffer_init(struct drm_device *dev, + struct qxl_framebuffer *qfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + + qfb->obj = obj; + ret = drm_framebuffer_init(dev, &qfb->base, &qxl_fb_funcs); + if (ret) { + qfb->obj = NULL; + return ret; + } + drm_helper_mode_fill_fb_struct(&qfb->base, mode_cmd); + return 0; +} + +static void qxl_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +} + +static bool qxl_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + + qxl_io_log(qdev, "%s: (%d,%d) => (%d,%d)\n", + __func__, + mode->hdisplay, mode->vdisplay, + adjusted_mode->hdisplay, + adjusted_mode->vdisplay); + return true; +} + +void +qxl_send_monitors_config(struct qxl_device *qdev) +{ + int i; + + BUG_ON(!qdev->ram_header->monitors_config); + + if (qdev->monitors_config->count == 0) { + qxl_io_log(qdev, "%s: 0 monitors??\n", __func__); + return; + } + for (i = 0 ; i < qdev->monitors_config->count ; ++i) { + struct qxl_head *head = &qdev->monitors_config->heads[i]; + + if (head->y > 8192 || head->y < head->x || + head->width > 8192 || head->height > 8192) { + DRM_ERROR("head %d wrong: %dx%d+%d+%d\n", + i, head->width, head->height, + head->x, head->y); + return; + } + } + qxl_io_monitors_config(qdev); +} + +static void qxl_monitors_config_set_single(struct qxl_device *qdev, + unsigned x, unsigned y, + unsigned width, unsigned height) +{ + DRM_DEBUG("%dx%d+%d+%d\n", width, height, x, y); + qdev->monitors_config->count = 1; + qdev->monitors_config->heads[0].x = x; + qdev->monitors_config->heads[0].y = y; + qdev->monitors_config->heads[0].width = width; + qdev->monitors_config->heads[0].height = height; +} + +static int qxl_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + struct qxl_mode *m = (void *)mode->private; + struct qxl_framebuffer *qfb; + struct qxl_bo *bo, *old_bo = NULL; + uint32_t width, height, base_offset; + bool recreate_primary = false; + int ret; + + if (!crtc->fb) { + DRM_DEBUG_KMS("No FB bound\n"); + return 0; + } + + if (old_fb) { + qfb = to_qxl_framebuffer(old_fb); + old_bo = gem_to_qxl_bo(qfb->obj); + } + qfb = to_qxl_framebuffer(crtc->fb); + bo = gem_to_qxl_bo(qfb->obj); + if (!m) + /* and do we care? */ + DRM_DEBUG("%dx%d: not a native mode\n", x, y); + else + DRM_DEBUG("%dx%d: qxl id %d\n", + mode->hdisplay, mode->vdisplay, m->id); + DRM_DEBUG("+%d+%d (%d,%d) => (%d,%d)\n", + x, y, + mode->hdisplay, mode->vdisplay, + adjusted_mode->hdisplay, + adjusted_mode->vdisplay); + + recreate_primary = true; + + width = mode->hdisplay; + height = mode->vdisplay; + base_offset = 0; + + ret = qxl_bo_reserve(bo, false); + if (ret != 0) + return ret; + ret = qxl_bo_pin(bo, bo->type, NULL); + if (ret != 0) { + qxl_bo_unreserve(bo); + return -EINVAL; + } + qxl_bo_unreserve(bo); + if (recreate_primary) { + qxl_io_destroy_primary(qdev); + qxl_io_log(qdev, + "recreate primary: %dx%d (was %dx%d,%d,%d)\n", + width, height, bo->surf.width, + bo->surf.height, bo->surf.stride, bo->surf.format); + qxl_io_create_primary(qdev, width, height, base_offset, bo); + bo->is_primary = true; + } + + if (old_bo && old_bo != bo) { + old_bo->is_primary = false; + ret = qxl_bo_reserve(old_bo, false); + qxl_bo_unpin(old_bo); + qxl_bo_unreserve(old_bo); + } + + if (qdev->monitors_config->count == 0) { + qxl_monitors_config_set_single(qdev, x, y, + mode->hdisplay, + mode->vdisplay); + } + qdev->mode_set = true; + return 0; +} + +static void qxl_crtc_prepare(struct drm_crtc *crtc) +{ + DRM_DEBUG("current: %dx%d+%d+%d (%d).\n", + crtc->mode.hdisplay, crtc->mode.vdisplay, + crtc->x, crtc->y, crtc->enabled); +} + +static void qxl_crtc_commit(struct drm_crtc *crtc) +{ + DRM_DEBUG("\n"); +} + +static void qxl_crtc_load_lut(struct drm_crtc *crtc) +{ + DRM_DEBUG("\n"); +} + +static const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs = { + .dpms = qxl_crtc_dpms, + .mode_fixup = qxl_crtc_mode_fixup, + .mode_set = qxl_crtc_mode_set, + .prepare = qxl_crtc_prepare, + .commit = qxl_crtc_commit, + .load_lut = qxl_crtc_load_lut, +}; + +static int qdev_crtc_init(struct drm_device *dev, int num_crtc) +{ + struct qxl_crtc *qxl_crtc; + + qxl_crtc = kzalloc(sizeof(struct qxl_crtc), GFP_KERNEL); + if (!qxl_crtc) + return -ENOMEM; + + drm_crtc_init(dev, &qxl_crtc->base, &qxl_crtc_funcs); + + drm_mode_crtc_set_gamma_size(&qxl_crtc->base, 256); + drm_crtc_helper_add(&qxl_crtc->base, &qxl_crtc_helper_funcs); + return 0; +} + +static void qxl_enc_dpms(struct drm_encoder *encoder, int mode) +{ + DRM_DEBUG("\n"); +} + +static bool qxl_enc_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG("\n"); + return true; +} + +static void qxl_enc_prepare(struct drm_encoder *encoder) +{ + DRM_DEBUG("\n"); +} + +static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev, + struct drm_encoder *encoder) +{ + int i; + struct qxl_head *head; + struct drm_display_mode *mode; + + BUG_ON(!encoder); + /* TODO: ugly, do better */ + for (i = 0 ; (encoder->possible_crtcs != (1 << i)) && i < 32; ++i) + ; + if (encoder->possible_crtcs != (1 << i)) { + DRM_ERROR("encoder has wrong possible_crtcs: %x\n", + encoder->possible_crtcs); + return; + } + if (!qdev->monitors_config || + qdev->monitors_config->max_allowed <= i) { + DRM_ERROR( + "head number too large or missing monitors config: %p, %d", + qdev->monitors_config, + qdev->monitors_config ? + qdev->monitors_config->max_allowed : -1); + return; + } + if (!encoder->crtc) { + DRM_ERROR("missing crtc on encoder %p\n", encoder); + return; + } + if (i != 0) + DRM_DEBUG("missing for multiple monitors: no head holes\n"); + head = &qdev->monitors_config->heads[i]; + head->id = i; + head->surface_id = 0; + if (encoder->crtc->enabled) { + mode = &encoder->crtc->mode; + head->width = mode->hdisplay; + head->height = mode->vdisplay; + head->x = encoder->crtc->x; + head->y = encoder->crtc->y; + if (qdev->monitors_config->count < i + 1) + qdev->monitors_config->count = i + 1; + } else { + head->width = 0; + head->height = 0; + head->x = 0; + head->y = 0; + } + DRM_DEBUG("setting head %d to +%d+%d %dx%d\n", + i, head->x, head->y, head->width, head->height); + head->flags = 0; + /* TODO - somewhere else to call this for multiple monitors + * (config_commit?) */ + qxl_send_monitors_config(qdev); +} + +static void qxl_enc_commit(struct drm_encoder *encoder) +{ + struct qxl_device *qdev = encoder->dev->dev_private; + + qxl_write_monitors_config_for_encoder(qdev, encoder); + DRM_DEBUG("\n"); +} + +static void qxl_enc_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG("\n"); +} + +static int qxl_conn_get_modes(struct drm_connector *connector) +{ + int ret = 0; + struct qxl_device *qdev = connector->dev->dev_private; + + DRM_DEBUG_KMS("monitors_config=%p\n", qdev->monitors_config); + /* TODO: what should we do here? only show the configured modes for the + * device, or allow the full list, or both? */ + if (qdev->monitors_config && qdev->monitors_config->count) { + ret = qxl_add_monitors_config_modes(connector); + if (ret < 0) + return ret; + } + ret += qxl_add_common_modes(connector); + return ret; +} + +static int qxl_conn_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* TODO: is this called for user defined modes? (xrandr --add-mode) + * TODO: check that the mode fits in the framebuffer */ + DRM_DEBUG("%s: %dx%d status=%d\n", mode->name, mode->hdisplay, + mode->vdisplay, mode->status); + return MODE_OK; +} + +static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector) +{ + struct qxl_output *qxl_output = + drm_connector_to_qxl_output(connector); + + DRM_DEBUG("\n"); + return &qxl_output->enc; +} + + +static const struct drm_encoder_helper_funcs qxl_enc_helper_funcs = { + .dpms = qxl_enc_dpms, + .mode_fixup = qxl_enc_mode_fixup, + .prepare = qxl_enc_prepare, + .mode_set = qxl_enc_mode_set, + .commit = qxl_enc_commit, +}; + +static const struct drm_connector_helper_funcs qxl_connector_helper_funcs = { + .get_modes = qxl_conn_get_modes, + .mode_valid = qxl_conn_mode_valid, + .best_encoder = qxl_best_encoder, +}; + +static void qxl_conn_save(struct drm_connector *connector) +{ + DRM_DEBUG("\n"); +} + +static void qxl_conn_restore(struct drm_connector *connector) +{ + DRM_DEBUG("\n"); +} + +static enum drm_connector_status qxl_conn_detect( + struct drm_connector *connector, + bool force) +{ + struct qxl_output *output = + drm_connector_to_qxl_output(connector); + struct drm_device *ddev = connector->dev; + struct qxl_device *qdev = ddev->dev_private; + int connected; + + /* The first monitor is always connected */ + connected = (output->index == 0) || + (qdev->monitors_config && + qdev->monitors_config->count > output->index); + + DRM_DEBUG("\n"); + return connected ? connector_status_connected + : connector_status_disconnected; +} + +static int qxl_conn_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + DRM_DEBUG("\n"); + return 0; +} + +static void qxl_conn_destroy(struct drm_connector *connector) +{ + struct qxl_output *qxl_output = + drm_connector_to_qxl_output(connector); + + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(qxl_output); +} + +static const struct drm_connector_funcs qxl_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = qxl_conn_save, + .restore = qxl_conn_restore, + .detect = qxl_conn_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = qxl_conn_set_property, + .destroy = qxl_conn_destroy, +}; + +static void qxl_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs qxl_enc_funcs = { + .destroy = qxl_enc_destroy, +}; + +static int qdev_output_init(struct drm_device *dev, int num_output) +{ + struct qxl_output *qxl_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + + qxl_output = kzalloc(sizeof(struct qxl_output), GFP_KERNEL); + if (!qxl_output) + return -ENOMEM; + + qxl_output->index = num_output; + + connector = &qxl_output->base; + encoder = &qxl_output->enc; + drm_connector_init(dev, &qxl_output->base, + &qxl_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); + + drm_encoder_init(dev, &qxl_output->enc, &qxl_enc_funcs, + DRM_MODE_ENCODER_VIRTUAL); + + encoder->possible_crtcs = 1 << num_output; + drm_mode_connector_attach_encoder(&qxl_output->base, + &qxl_output->enc); + drm_encoder_helper_add(encoder, &qxl_enc_helper_funcs); + drm_connector_helper_add(connector, &qxl_connector_helper_funcs); + + drm_sysfs_connector_add(connector); + return 0; +} + +static struct drm_framebuffer * +qxl_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj; + struct qxl_framebuffer *qxl_fb; + struct qxl_device *qdev = dev->dev_private; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); + + qxl_fb = kzalloc(sizeof(*qxl_fb), GFP_KERNEL); + if (qxl_fb == NULL) + return NULL; + + ret = qxl_framebuffer_init(dev, qxl_fb, mode_cmd, obj); + if (ret) { + kfree(qxl_fb); + drm_gem_object_unreference_unlocked(obj); + return NULL; + } + + if (qdev->active_user_framebuffer) { + DRM_INFO("%s: active_user_framebuffer %p -> %p\n", + __func__, + qdev->active_user_framebuffer, qxl_fb); + } + qdev->active_user_framebuffer = qxl_fb; + + return &qxl_fb->base; +} + +static const struct drm_mode_config_funcs qxl_mode_funcs = { + .fb_create = qxl_user_framebuffer_create, +}; + +int qxl_modeset_init(struct qxl_device *qdev) +{ + int i; + int ret; + struct drm_gem_object *gobj; + int max_allowed = QXL_NUM_OUTPUTS; + int monitors_config_size = sizeof(struct qxl_monitors_config) + + max_allowed * sizeof(struct qxl_head); + + drm_mode_config_init(qdev->ddev); + ret = qxl_gem_object_create(qdev, monitors_config_size, 0, + QXL_GEM_DOMAIN_VRAM, + false, false, NULL, &gobj); + if (ret) { + DRM_ERROR("%s: failed to create gem ret=%d\n", __func__, ret); + return -ENOMEM; + } + qdev->monitors_config_bo = gem_to_qxl_bo(gobj); + qxl_bo_kmap(qdev->monitors_config_bo, NULL); + qdev->monitors_config = qdev->monitors_config_bo->kptr; + qdev->ram_header->monitors_config = + qxl_bo_physical_address(qdev, qdev->monitors_config_bo, 0); + + memset(qdev->monitors_config, 0, monitors_config_size); + qdev->monitors_config->max_allowed = max_allowed; + + qdev->ddev->mode_config.funcs = (void *)&qxl_mode_funcs; + + /* modes will be validated against the framebuffer size */ + qdev->ddev->mode_config.min_width = 320; + qdev->ddev->mode_config.min_height = 200; + qdev->ddev->mode_config.max_width = 8192; + qdev->ddev->mode_config.max_height = 8192; + + qdev->ddev->mode_config.fb_base = qdev->vram_base; + for (i = 0 ; i < QXL_NUM_OUTPUTS; ++i) { + qdev_crtc_init(qdev->ddev, i); + qdev_output_init(qdev->ddev, i); + } + + qdev->mode_info.mode_config_initialized = true; + + /* primary surface must be created by this point, to allow + * issuing command queue commands and having them read by + * spice server. */ + qxl_fbdev_init(qdev); + return 0; +} + +void qxl_modeset_fini(struct qxl_device *qdev) +{ + qxl_fbdev_fini(qdev); + if (qdev->mode_info.mode_config_initialized) { + drm_mode_config_cleanup(qdev->ddev); + qdev->mode_info.mode_config_initialized = false; + } +} diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c new file mode 100644 index 0000000..3c8c3db --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -0,0 +1,390 @@ +/* + * Copyright 2011 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "qxl_drv.h" +#include "qxl_object.h" + +/* returns a pointer to the already allocated qxl_rect array inside + * the qxl_clip_rects. This is *not* the same as the memory allocated + * on the device, it is offset to qxl_clip_rects.chunk.data */ +static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev, + struct qxl_drawable *drawable, + unsigned num_clips, + struct qxl_bo **clips_bo, + struct qxl_release *release) +{ + struct qxl_clip_rects *dev_clips; + int ret; + int size = sizeof(*dev_clips) + sizeof(struct qxl_rect) * num_clips; + ret = qxl_alloc_bo_reserved(qdev, size, clips_bo); + if (ret) + return NULL; + + ret = qxl_bo_kmap(*clips_bo, (void **)&dev_clips); + if (ret) { + qxl_bo_unref(clips_bo); + return NULL; + } + dev_clips->num_rects = num_clips; + dev_clips->chunk.next_chunk = 0; + dev_clips->chunk.prev_chunk = 0; + dev_clips->chunk.data_size = sizeof(struct qxl_rect) * num_clips; + return (struct qxl_rect *)dev_clips->chunk.data; +} + +static int +make_drawable(struct qxl_device *qdev, int surface, uint8_t type, + const struct qxl_rect *rect, + struct qxl_release **release) +{ + struct qxl_drawable *drawable; + int i, ret; + + ret = qxl_alloc_release_reserved(qdev, sizeof(*drawable), + QXL_RELEASE_DRAWABLE, release, + NULL); + if (ret) + return ret; + + drawable = (struct qxl_drawable *)qxl_release_map(qdev, *release); + drawable->type = type; + + drawable->surface_id = surface; /* Only primary for now */ + drawable->effect = QXL_EFFECT_OPAQUE; + drawable->self_bitmap = 0; + drawable->self_bitmap_area.top = 0; + drawable->self_bitmap_area.left = 0; + drawable->self_bitmap_area.bottom = 0; + drawable->self_bitmap_area.right = 0; + /* FIXME: add clipping */ + drawable->clip.type = SPICE_CLIP_TYPE_NONE; + + /* + * surfaces_dest[i] should apparently be filled out with the + * surfaces that we depend on, and surface_rects should be + * filled with the rectangles of those surfaces that we + * are going to use. + */ + for (i = 0; i < 3; ++i) + drawable->surfaces_dest[i] = -1; + + if (rect) + drawable->bbox = *rect; + + drawable->mm_time = qdev->rom->mm_clock; + qxl_release_unmap(qdev, *release, &drawable->release_info); + return 0; +} + +static int qxl_palette_create_1bit(struct qxl_bo **palette_bo, + const struct qxl_fb_image *qxl_fb_image) +{ + struct qxl_device *qdev = qxl_fb_image->qdev; + const struct fb_image *fb_image = &qxl_fb_image->fb_image; + uint32_t visual = qxl_fb_image->visual; + const uint32_t *pseudo_palette = qxl_fb_image->pseudo_palette; + struct qxl_palette *pal; + int ret; + uint32_t fgcolor, bgcolor; + static uint64_t unique; /* we make no attempt to actually set this + * correctly globaly, since that would require + * tracking all of our palettes. */ + + ret = qxl_alloc_bo_reserved(qdev, + sizeof(struct qxl_palette) + sizeof(uint32_t) * 2, + palette_bo); + + ret = qxl_bo_kmap(*palette_bo, (void **)&pal); + pal->num_ents = 2; + pal->unique = unique++; + if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) { + /* NB: this is the only used branch currently. */ + fgcolor = pseudo_palette[fb_image->fg_color]; + bgcolor = pseudo_palette[fb_image->bg_color]; + } else { + fgcolor = fb_image->fg_color; + bgcolor = fb_image->bg_color; + } + pal->ents[0] = bgcolor; + pal->ents[1] = fgcolor; + qxl_bo_kunmap(*palette_bo); + return 0; +} + +void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, + int stride /* filled in if 0 */) +{ + struct qxl_device *qdev = qxl_fb_image->qdev; + struct qxl_drawable *drawable; + struct qxl_rect rect; + const struct fb_image *fb_image = &qxl_fb_image->fb_image; + int x = fb_image->dx; + int y = fb_image->dy; + int width = fb_image->width; + int height = fb_image->height; + const char *src = fb_image->data; + int depth = fb_image->depth; + struct qxl_release *release; + struct qxl_bo *image_bo; + struct qxl_image *image; + int ret; + + if (stride == 0) + stride = depth * width / 8; + + rect.left = x; + rect.right = x + width; + rect.top = y; + rect.bottom = y + height; + + ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, &release); + if (ret) + return; + + ret = qxl_image_create(qdev, release, &image_bo, + (const uint8_t *)src, 0, 0, + width, height, depth, stride); + if (ret) { + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); + return; + } + + if (depth == 1) { + struct qxl_bo *palette_bo; + void *ptr; + ret = qxl_palette_create_1bit(&palette_bo, qxl_fb_image); + qxl_release_add_res(qdev, release, palette_bo); + + ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0); + image = ptr; + image->u.bitmap.palette = + qxl_bo_physical_address(qdev, palette_bo, 0); + qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr); + qxl_bo_unreserve(palette_bo); + qxl_bo_unref(&palette_bo); + } + + drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); + + drawable->u.copy.src_area.top = 0; + drawable->u.copy.src_area.bottom = height; + drawable->u.copy.src_area.left = 0; + drawable->u.copy.src_area.right = width; + + drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; + drawable->u.copy.scale_mode = 0; + drawable->u.copy.mask.flags = 0; + drawable->u.copy.mask.pos.x = 0; + drawable->u.copy.mask.pos.y = 0; + drawable->u.copy.mask.bitmap = 0; + + drawable->u.copy.src_bitmap = + qxl_bo_physical_address(qdev, image_bo, 0); + qxl_release_unmap(qdev, release, &drawable->release_info); + + qxl_release_add_res(qdev, release, image_bo); + qxl_bo_unreserve(image_bo); + qxl_bo_unref(&image_bo); + + qxl_fence_releaseable(qdev, release); + qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); + qxl_release_unreserve(qdev, release); +} + +/* push a draw command using the given clipping rectangles as + * the sources from the shadow framebuffer. + * + * Right now implementing with a single draw and a clip list. Clip + * lists are known to be a problem performance wise, this can be solved + * by treating them differently in the server. + */ +void qxl_draw_dirty_fb(struct qxl_device *qdev, + struct qxl_framebuffer *qxl_fb, + struct qxl_bo *bo, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int inc) +{ + /* + * TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should + * send a fill command instead, much cheaper. + * + * See include/drm/drm_mode.h + */ + struct drm_clip_rect *clips_ptr; + int i; + int left, right, top, bottom; + int width, height; + struct qxl_drawable *drawable; + struct qxl_rect drawable_rect; + struct qxl_rect *rects; + int stride = qxl_fb->base.pitches[0]; + /* depth is not actually interesting, we don't mask with it */ + int depth = qxl_fb->base.bits_per_pixel; + uint8_t *surface_base; + struct qxl_release *release; + struct qxl_bo *image_bo; + struct qxl_bo *clips_bo; + int ret; + + left = clips->x1; + right = clips->x2; + top = clips->y1; + bottom = clips->y2; + + /* skip the first clip rect */ + for (i = 1, clips_ptr = clips + inc; + i < num_clips; i++, clips_ptr += inc) { + left = min_t(int, left, (int)clips_ptr->x1); + right = max_t(int, right, (int)clips_ptr->x2); + top = min_t(int, top, (int)clips_ptr->y1); + bottom = max_t(int, bottom, (int)clips_ptr->y2); + } + + width = right - left; + height = bottom - top; + drawable_rect.left = left; + drawable_rect.right = right; + drawable_rect.top = top; + drawable_rect.bottom = bottom; + ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &drawable_rect, + &release); + if (ret) + return; + + ret = qxl_bo_kmap(bo, (void **)&surface_base); + if (ret) + goto out_unref; + + ret = qxl_image_create(qdev, release, &image_bo, surface_base, + left, top, width, height, depth, stride); + qxl_bo_kunmap(bo); + if (ret) + goto out_unref; + + rects = drawable_set_clipping(qdev, drawable, num_clips, &clips_bo, release); + if (!rects) { + qxl_bo_unref(&image_bo); + goto out_unref; + } + drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); + + drawable->clip.type = SPICE_CLIP_TYPE_RECTS; + drawable->clip.data = qxl_bo_physical_address(qdev, + clips_bo, 0); + qxl_release_add_res(qdev, release, clips_bo); + + drawable->u.copy.src_area.top = 0; + drawable->u.copy.src_area.bottom = height; + drawable->u.copy.src_area.left = 0; + drawable->u.copy.src_area.right = width; + + drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; + drawable->u.copy.scale_mode = 0; + drawable->u.copy.mask.flags = 0; + drawable->u.copy.mask.pos.x = 0; + drawable->u.copy.mask.pos.y = 0; + drawable->u.copy.mask.bitmap = 0; + + drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, image_bo, 0); + qxl_release_unmap(qdev, release, &drawable->release_info); + qxl_release_add_res(qdev, release, image_bo); + qxl_bo_unreserve(image_bo); + qxl_bo_unref(&image_bo); + clips_ptr = clips; + for (i = 0; i < num_clips; i++, clips_ptr += inc) { + rects[i].left = clips_ptr->x1; + rects[i].right = clips_ptr->x2; + rects[i].top = clips_ptr->y1; + rects[i].bottom = clips_ptr->y2; + } + qxl_bo_kunmap(clips_bo); + qxl_bo_unreserve(clips_bo); + qxl_bo_unref(&clips_bo); + + qxl_fence_releaseable(qdev, release); + qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); + qxl_release_unreserve(qdev, release); + return; + +out_unref: + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); +} + +void qxl_draw_copyarea(struct qxl_device *qdev, + u32 width, u32 height, + u32 sx, u32 sy, + u32 dx, u32 dy) +{ + struct qxl_drawable *drawable; + struct qxl_rect rect; + struct qxl_release *release; + int ret; + + rect.left = dx; + rect.top = dy; + rect.right = dx + width; + rect.bottom = dy + height; + ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, &release); + if (ret) + return; + + drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); + drawable->u.copy_bits.src_pos.x = sx; + drawable->u.copy_bits.src_pos.y = sy; + + qxl_release_unmap(qdev, release, &drawable->release_info); + qxl_fence_releaseable(qdev, release); + qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); + qxl_release_unreserve(qdev, release); +} + +void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec) +{ + struct qxl_device *qdev = qxl_draw_fill_rec->qdev; + struct qxl_rect rect = qxl_draw_fill_rec->rect; + uint32_t color = qxl_draw_fill_rec->color; + uint16_t rop = qxl_draw_fill_rec->rop; + struct qxl_drawable *drawable; + struct qxl_release *release; + int ret; + + ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, &release); + if (ret) + return; + + drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); + drawable->u.fill.brush.type = SPICE_BRUSH_TYPE_SOLID; + drawable->u.fill.brush.u.color = color; + drawable->u.fill.rop_descriptor = rop; + drawable->u.fill.mask.flags = 0; + drawable->u.fill.mask.pos.x = 0; + drawable->u.fill.mask.pos.y = 0; + drawable->u.fill.mask.bitmap = 0; + + qxl_release_unmap(qdev, release, &drawable->release_info); + qxl_fence_releaseable(qdev, release); + qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); + qxl_release_unreserve(qdev, release); +} diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c new file mode 100644 index 0000000..aa291d8 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -0,0 +1,145 @@ +/* vim: set ts=8 sw=8 tw=78 ai noexpandtab */ +/* qxl_drv.c -- QXL driver -*- linux-c -*- + * + * Copyright 2011 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Dave Airlie <airlie@redhat.com> + * Alon Levy <alevy@redhat.com> + */ + +#include <linux/module.h> +#include <linux/console.h> + +#include "drmP.h" +#include "drm/drm.h" + +#include "qxl_drv.h" + +extern int qxl_max_ioctls; +static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { + { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, + 0xffff00, 0 }, + { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_OTHER << 8, + 0xffff00, 0 }, + { 0, 0, 0 }, +}; +MODULE_DEVICE_TABLE(pci, pciidlist); + +static int qxl_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, qxl_modeset, int, 0400); + +static struct drm_driver qxl_driver; +static struct pci_driver qxl_pci_driver; + +static int +qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + if (pdev->revision < 4) { + DRM_ERROR("qxl too old, doesn't support client_monitors_config," + " use xf86-video-qxl in user mode"); + return -EINVAL; /* TODO: ENODEV ? */ + } + return drm_get_pci_dev(pdev, ent, &qxl_driver); +} + +static void +qxl_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static struct pci_driver qxl_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = qxl_pci_probe, + .remove = qxl_pci_remove, +}; + +static const struct file_operations qxl_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .poll = drm_poll, + .fasync = drm_fasync, + .mmap = qxl_mmap, +}; + +static struct drm_driver qxl_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, + .dev_priv_size = 0, + .load = qxl_driver_load, + .unload = qxl_driver_unload, + + .dumb_create = qxl_mode_dumb_create, + .dumb_map_offset = qxl_mode_dumb_mmap, + .dumb_destroy = qxl_mode_dumb_destroy, +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = qxl_debugfs_init, + .debugfs_cleanup = qxl_debugfs_takedown, +#endif + .gem_init_object = qxl_gem_object_init, + .gem_free_object = qxl_gem_object_free, + .gem_open_object = qxl_gem_object_open, + .gem_close_object = qxl_gem_object_close, + .fops = &qxl_fops, + .ioctls = qxl_ioctls, + .irq_handler = qxl_irq_handler, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = 0, + .minor = 1, + .patchlevel = 0, +}; + +static int __init qxl_init(void) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && qxl_modeset == -1) + return -EINVAL; +#endif + + if (qxl_modeset == 0) + return -EINVAL; + qxl_driver.num_ioctls = qxl_max_ioctls; + return drm_pci_init(&qxl_driver, &qxl_pci_driver); +} + +static void __exit qxl_exit(void) +{ + drm_pci_exit(&qxl_driver, &qxl_pci_driver); +} + +module_init(qxl_init); +module_exit(qxl_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h new file mode 100644 index 0000000..52b582c --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -0,0 +1,566 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + + +#ifndef QXL_DRV_H +#define QXL_DRV_H + +/* + * Definitions taken from spice-protocol, plus kernel driver specific bits. + */ + +#include <linux/workqueue.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> + +#include "drmP.h" +#include "drm_crtc.h" +#include <ttm/ttm_bo_api.h> +#include <ttm/ttm_bo_driver.h> +#include <ttm/ttm_placement.h> +#include <ttm/ttm_module.h> + +#include <drm/qxl_drm.h> +#include "qxl_dev.h" + +#define DRIVER_AUTHOR "Dave Airlie" + +#define DRIVER_NAME "qxl" +#define DRIVER_DESC "RH QXL" +#define DRIVER_DATE "20120117" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 0 + +#define QXL_NUM_OUTPUTS 1 + +#define QXL_DEBUGFS_MAX_COMPONENTS 32 + +extern int qxl_log_level; + +enum { + QXL_INFO_LEVEL = 1, + QXL_DEBUG_LEVEL = 2, +}; + +#define QXL_INFO(qdev, fmt, ...) do { \ + if (qxl_log_level >= QXL_INFO_LEVEL) { \ + qxl_io_log(qdev, fmt, __VA_ARGS__); \ + } \ + } while (0) +#define QXL_DEBUG(qdev, fmt, ...) do { \ + if (qxl_log_level >= QXL_DEBUG_LEVEL) { \ + qxl_io_log(qdev, fmt, __VA_ARGS__); \ + } \ + } while (0) +#define QXL_INFO_ONCE(qdev, fmt, ...) do { \ + static int done; \ + if (!done) { \ + done = 1; \ + QXL_INFO(qdev, fmt, __VA_ARGS__); \ + } \ + } while (0) + +#define DRM_FILE_OFFSET 0x100000000ULL +#define DRM_FILE_PAGE_OFFSET (DRM_FILE_OFFSET >> PAGE_SHIFT) + +#define QXL_INTERRUPT_MASK (\ + QXL_INTERRUPT_DISPLAY |\ + QXL_INTERRUPT_CURSOR |\ + QXL_INTERRUPT_IO_CMD |\ + QXL_INTERRUPT_CLIENT_MONITORS_CONFIG) + +struct qxl_fence { + struct qxl_device *qdev; + uint32_t num_active_releases; + uint32_t *release_ids; + struct radix_tree_root tree; +}; + +struct qxl_bo { + /* Protected by gem.mutex */ + struct list_head list; + /* Protected by tbo.reserved */ + u32 placements[3]; + struct ttm_placement placement; + struct ttm_buffer_object tbo; + struct ttm_bo_kmap_obj kmap; + unsigned pin_count; + void *kptr; + int type; + /* Constant after initialization */ + struct drm_gem_object gem_base; + bool is_primary; /* is this now a primary surface */ + bool hw_surf_alloc; + struct qxl_surface surf; + uint32_t surface_id; + struct qxl_fence fence; /* per bo fence - list of releases */ + struct qxl_release *surf_create; + atomic_t reserve_count; +}; +#define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base) + +struct qxl_gem { + struct mutex mutex; + struct list_head objects; +}; + +struct qxl_bo_list { + struct list_head lhead; + struct qxl_bo *bo; +}; + +struct qxl_reloc_list { + struct list_head bos; +}; + +struct qxl_crtc { + struct drm_crtc base; + int cur_x; + int cur_y; +}; + +struct qxl_output { + int index; + struct drm_connector base; + struct drm_encoder enc; +}; + +struct qxl_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; +}; + +#define to_qxl_crtc(x) container_of(x, struct qxl_crtc, base) +#define drm_connector_to_qxl_output(x) container_of(x, struct qxl_output, base) +#define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, base) +#define to_qxl_framebuffer(x) container_of(x, struct qxl_framebuffer, base) + +struct qxl_mman { + struct ttm_bo_global_ref bo_global_ref; + struct drm_global_reference mem_global_ref; + bool mem_global_referenced; + struct ttm_bo_device bdev; +}; + +struct qxl_mode_info { + int num_modes; + struct qxl_mode *modes; + bool mode_config_initialized; + + /* pointer to fbdev info structure */ + struct qxl_fbdev *qfbdev; +}; + + +struct qxl_memslot { + uint8_t generation; + uint64_t start_phys_addr; + uint64_t end_phys_addr; + uint64_t high_bits; +}; + +enum { + QXL_RELEASE_DRAWABLE, + QXL_RELEASE_SURFACE_CMD, + QXL_RELEASE_CURSOR_CMD, +}; + +/* drm_ prefix to differentiate from qxl_release_info in + * spice-protocol/qxl_dev.h */ +#define QXL_MAX_RES 96 +struct qxl_release { + int id; + int type; + int bo_count; + uint32_t release_offset; + uint32_t surface_release_id; + struct qxl_bo *bos[QXL_MAX_RES]; +}; + +struct qxl_fb_image { + struct qxl_device *qdev; + uint32_t pseudo_palette[16]; + struct fb_image fb_image; + uint32_t visual; +}; + +struct qxl_draw_fill { + struct qxl_device *qdev; + struct qxl_rect rect; + uint32_t color; + uint16_t rop; +}; + +/* + * Debugfs + */ +struct qxl_debugfs { + struct drm_info_list *files; + unsigned num_files; +}; + +int qxl_debugfs_add_files(struct qxl_device *rdev, + struct drm_info_list *files, + unsigned nfiles); +int qxl_debugfs_fence_init(struct qxl_device *rdev); +void qxl_debugfs_remove_files(struct qxl_device *qdev); + +struct qxl_device; + +struct qxl_device { + struct device *dev; + struct drm_device *ddev; + struct pci_dev *pdev; + unsigned long flags; + + resource_size_t vram_base, vram_size; + resource_size_t surfaceram_base, surfaceram_size; + resource_size_t rom_base, rom_size; + struct qxl_rom *rom; + + struct qxl_mode *modes; + struct qxl_bo *monitors_config_bo; + struct qxl_monitors_config *monitors_config; + + /* last received client_monitors_config */ + struct qxl_monitors_config *client_monitors_config; + + int io_base; + void *ram; + struct qxl_mman mman; + struct qxl_gem gem; + struct qxl_mode_info mode_info; + + /* + * last created framebuffer with fb_create + * only used by debugfs dumbppm + */ + struct qxl_framebuffer *active_user_framebuffer; + + struct fb_info *fbdev_info; + struct qxl_framebuffer *fbdev_qfb; + void *ram_physical; + + struct qxl_ring *release_ring; + struct qxl_ring *command_ring; + struct qxl_ring *cursor_ring; + + struct qxl_ram_header *ram_header; + bool mode_set; + + bool primary_created; + + struct qxl_memslot *mem_slots; + uint8_t n_mem_slots; + + uint8_t main_mem_slot; + uint8_t surfaces_mem_slot; + uint8_t slot_id_bits; + uint8_t slot_gen_bits; + uint64_t va_slot_mask; + + struct idr release_idr; + spinlock_t release_idr_lock; + struct mutex async_io_mutex; + unsigned int last_sent_io_cmd; + + /* interrupt handling */ + atomic_t irq_received; + atomic_t irq_received_display; + atomic_t irq_received_cursor; + atomic_t irq_received_io_cmd; + unsigned irq_received_error; + wait_queue_head_t display_event; + wait_queue_head_t cursor_event; + wait_queue_head_t io_cmd_event; + struct work_struct client_monitors_config_work; + + /* debugfs */ + struct qxl_debugfs debugfs[QXL_DEBUGFS_MAX_COMPONENTS]; + unsigned debugfs_count; + + struct mutex update_area_mutex; + + struct idr surf_id_idr; + spinlock_t surf_id_idr_lock; + int last_alloced_surf_id; + + struct mutex surf_evict_mutex; + struct io_mapping *vram_mapping; + struct io_mapping *surface_mapping; + + /* */ + struct mutex release_mutex; + struct qxl_bo *current_release_bo[3]; + int current_release_bo_offset[3]; + + struct workqueue_struct *gc_queue; + struct work_struct gc_work; + +}; + +/* forward declaration for QXL_INFO_IO */ +void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...); + +extern struct drm_ioctl_desc qxl_ioctls[]; +extern int qxl_max_ioctl; + +int qxl_driver_load(struct drm_device *dev, unsigned long flags); +int qxl_driver_unload(struct drm_device *dev); + +int qxl_modeset_init(struct qxl_device *qdev); +void qxl_modeset_fini(struct qxl_device *qdev); + +int qxl_bo_init(struct qxl_device *qdev); +void qxl_bo_fini(struct qxl_device *qdev); + +struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header, + int element_size, + int n_elements, + int prod_notify, + bool set_prod_notify, + wait_queue_head_t *push_event); +void qxl_ring_free(struct qxl_ring *ring); + +static inline void * +qxl_fb_virtual_address(struct qxl_device *qdev, unsigned long physical) +{ + QXL_INFO(qdev, "not implemented (%lu)\n", physical); + return 0; +} + +static inline uint64_t +qxl_bo_physical_address(struct qxl_device *qdev, struct qxl_bo *bo, + unsigned long offset) +{ + int slot_id = bo->type == QXL_GEM_DOMAIN_VRAM ? qdev->main_mem_slot : qdev->surfaces_mem_slot; + struct qxl_memslot *slot = &(qdev->mem_slots[slot_id]); + + /* TODO - need to hold one of the locks to read tbo.offset */ + return slot->high_bits | (bo->tbo.offset + offset); +} + +/* qxl_fb.c */ +#define QXLFB_CONN_LIMIT 1 + +int qxl_fbdev_init(struct qxl_device *qdev); +void qxl_fbdev_fini(struct qxl_device *qdev); +int qxl_get_handle_for_primary_fb(struct qxl_device *qdev, + struct drm_file *file_priv, + uint32_t *handle); + +/* qxl_display.c */ +int +qxl_framebuffer_init(struct drm_device *dev, + struct qxl_framebuffer *rfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); +void qxl_display_read_client_monitors_config(struct qxl_device *qdev); +void qxl_send_monitors_config(struct qxl_device *qdev); + +/* used by qxl_debugfs only */ +void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev); +void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count); + +/* qxl_gem.c */ +int qxl_gem_init(struct qxl_device *qdev); +void qxl_gem_fini(struct qxl_device *qdev); +int qxl_gem_object_create(struct qxl_device *qdev, int size, + int alignment, int initial_domain, + bool discardable, bool kernel, + struct qxl_surface *surf, + struct drm_gem_object **obj); +int qxl_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain, + uint64_t *gpu_addr); +void qxl_gem_object_unpin(struct drm_gem_object *obj); +int qxl_gem_object_create_with_handle(struct qxl_device *qdev, + struct drm_file *file_priv, + u32 domain, + size_t size, + struct qxl_surface *surf, + struct qxl_bo **qobj, + uint32_t *handle); +int qxl_gem_object_init(struct drm_gem_object *obj); +void qxl_gem_object_free(struct drm_gem_object *gobj); +int qxl_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv); +void qxl_gem_object_close(struct drm_gem_object *obj, + struct drm_file *file_priv); +void qxl_bo_force_delete(struct qxl_device *qdev); +int qxl_bo_kmap(struct qxl_bo *bo, void **ptr); + +/* qxl_dumb.c */ +int qxl_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int qxl_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); +int qxl_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); + + +/* qxl ttm */ +int qxl_ttm_init(struct qxl_device *qdev); +void qxl_ttm_fini(struct qxl_device *qdev); +int qxl_mmap(struct file *filp, struct vm_area_struct *vma); + +/* qxl image */ + +int qxl_image_create(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_bo **image_bo, + const uint8_t *data, + int x, int y, int width, int height, + int depth, int stride); +void qxl_update_screen(struct qxl_device *qxl); + +/* qxl io operations (qxl_cmd.c) */ + +void qxl_io_create_primary(struct qxl_device *qdev, + unsigned width, unsigned height, unsigned offset, + struct qxl_bo *bo); +void qxl_io_destroy_primary(struct qxl_device *qdev); +void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id); +void qxl_io_notify_oom(struct qxl_device *qdev); + +int qxl_io_update_area(struct qxl_device *qdev, struct qxl_bo *surf, + const struct qxl_rect *area); + +void qxl_io_reset(struct qxl_device *qdev); +void qxl_io_monitors_config(struct qxl_device *qdev); +int qxl_ring_push(struct qxl_ring *ring, const void *new_elt, bool interruptible); +void qxl_io_flush_release(struct qxl_device *qdev); +void qxl_io_flush_surfaces(struct qxl_device *qdev); + +int qxl_release_reserve(struct qxl_device *qdev, + struct qxl_release *release, bool no_wait); +void qxl_release_unreserve(struct qxl_device *qdev, + struct qxl_release *release); +union qxl_release_info *qxl_release_map(struct qxl_device *qdev, + struct qxl_release *release); +void qxl_release_unmap(struct qxl_device *qdev, + struct qxl_release *release, + union qxl_release_info *info); +/* + * qxl_bo_add_resource. + * + */ +void qxl_bo_add_resource(struct qxl_bo *main_bo, struct qxl_bo *resource); + +int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, + enum qxl_surface_cmd_type surface_cmd_type, + struct qxl_release *create_rel, + struct qxl_release **release); +int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, + int type, struct qxl_release **release, + struct qxl_bo **rbo); +int qxl_fence_releaseable(struct qxl_device *qdev, + struct qxl_release *release); +int +qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release, + uint32_t type, bool interruptible); +int +qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release, + uint32_t type, bool interruptible); +int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size, + struct qxl_bo **_bo); +/* qxl drawing commands */ + +void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, + int stride /* filled in if 0 */); + +void qxl_draw_dirty_fb(struct qxl_device *qdev, + struct qxl_framebuffer *qxl_fb, + struct qxl_bo *bo, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int inc); + +void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec); + +void qxl_draw_copyarea(struct qxl_device *qdev, + u32 width, u32 height, + u32 sx, u32 sy, + u32 dx, u32 dy); + +uint64_t +qxl_release_alloc(struct qxl_device *qdev, int type, + struct qxl_release **ret); + +void qxl_release_free(struct qxl_device *qdev, + struct qxl_release *release); +void qxl_release_add_res(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_bo *bo); +/* used by qxl_debugfs_release */ +struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev, + uint64_t id); + +bool qxl_queue_garbage_collect(struct qxl_device *qdev, bool flush); +int qxl_garbage_collect(struct qxl_device *qdev); + +/* debugfs */ + +int qxl_debugfs_init(struct drm_minor *minor); +void qxl_debugfs_takedown(struct drm_minor *minor); + +/* qxl_irq.c */ +int qxl_irq_init(struct qxl_device *qdev); +irqreturn_t qxl_irq_handler(DRM_IRQ_ARGS); + +/* qxl_fb.c */ +int qxl_fb_init(struct qxl_device *qdev); + +int qxl_debugfs_add_files(struct qxl_device *qdev, + struct drm_info_list *files, + unsigned nfiles); + +int qxl_surface_id_alloc(struct qxl_device *qdev, + struct qxl_bo *surf); +void qxl_surface_id_dealloc(struct qxl_device *qdev, + uint32_t surface_id); +int qxl_hw_surface_alloc(struct qxl_device *qdev, + struct qxl_bo *surf, + struct ttm_mem_reg *mem); +int qxl_hw_surface_dealloc(struct qxl_device *qdev, + struct qxl_bo *surf); + +int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo); + +struct qxl_drv_surface * +qxl_surface_lookup(struct drm_device *dev, int surface_id); +void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freeing); +int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf); + +/* qxl_fence.c */ +int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id); +int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id); +int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence); +void qxl_fence_fini(struct qxl_fence *qfence); + +#endif diff --git a/drivers/gpu/drm/qxl/qxl_dumb.c b/drivers/gpu/drm/qxl/qxl_dumb.c new file mode 100644 index 0000000..847c4ee --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_dumb.c @@ -0,0 +1,93 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include "qxl_drv.h" +#include "qxl_object.h" + +/* dumb ioctls implementation */ + +int qxl_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct qxl_device *qdev = dev->dev_private; + struct qxl_bo *qobj; + uint32_t handle; + int r; + struct qxl_surface surf; + uint32_t pitch, format; + pitch = args->width * ((args->bpp + 1) / 8); + args->size = pitch * args->height; + args->size = ALIGN(args->size, PAGE_SIZE); + + switch (args->bpp) { + case 16: + format = SPICE_SURFACE_FMT_16_565; + break; + case 32: + format = SPICE_SURFACE_FMT_32_xRGB; + break; + default: + return -EINVAL; + } + + surf.width = args->width; + surf.height = args->height; + surf.stride = pitch; + surf.format = format; + r = qxl_gem_object_create_with_handle(qdev, file_priv, + QXL_GEM_DOMAIN_VRAM, + args->size, &surf, &qobj, + &handle); + if (r) + return r; + args->pitch = pitch; + args->handle = handle; + return 0; +} + +int qxl_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} + +int qxl_mode_dumb_mmap(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p) +{ + struct drm_gem_object *gobj; + struct qxl_bo *qobj; + + BUG_ON(!offset_p); + gobj = drm_gem_object_lookup(dev, file_priv, handle); + if (gobj == NULL) + return -ENOENT; + qobj = gem_to_qxl_bo(gobj); + *offset_p = qxl_bo_mmap_offset(qobj); + drm_gem_object_unreference_unlocked(gobj); + return 0; +} diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c new file mode 100644 index 0000000..b3c5127 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -0,0 +1,567 @@ +/* + * Copyright © 2013 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * David Airlie + */ +#include <linux/module.h> +#include <linux/fb.h> + +#include "drmP.h" +#include "drm/drm.h" +#include "drm/drm_crtc.h" +#include "drm/drm_crtc_helper.h" +#include "qxl_drv.h" + +#include "qxl_object.h" +#include "drm_fb_helper.h" + +#define QXL_DIRTY_DELAY (HZ / 30) + +struct qxl_fbdev { + struct drm_fb_helper helper; + struct qxl_framebuffer qfb; + struct list_head fbdev_list; + struct qxl_device *qdev; + + void *shadow; + int size; + + /* dirty memory logging */ + struct { + spinlock_t lock; + bool active; + unsigned x1; + unsigned y1; + unsigned x2; + unsigned y2; + } dirty; +}; + +static void qxl_fb_image_init(struct qxl_fb_image *qxl_fb_image, + struct qxl_device *qdev, struct fb_info *info, + const struct fb_image *image) +{ + qxl_fb_image->qdev = qdev; + if (info) { + qxl_fb_image->visual = info->fix.visual; + if (qxl_fb_image->visual == FB_VISUAL_TRUECOLOR || + qxl_fb_image->visual == FB_VISUAL_DIRECTCOLOR) + memcpy(&qxl_fb_image->pseudo_palette, + info->pseudo_palette, + sizeof(qxl_fb_image->pseudo_palette)); + } else { + /* fallback */ + if (image->depth == 1) + qxl_fb_image->visual = FB_VISUAL_MONO10; + else + qxl_fb_image->visual = FB_VISUAL_DIRECTCOLOR; + } + if (image) { + memcpy(&qxl_fb_image->fb_image, image, + sizeof(qxl_fb_image->fb_image)); + } +} + +static void qxl_fb_dirty_flush(struct fb_info *info) +{ + struct qxl_fbdev *qfbdev = info->par; + struct qxl_device *qdev = qfbdev->qdev; + struct qxl_fb_image qxl_fb_image; + struct fb_image *image = &qxl_fb_image.fb_image; + u32 x1, x2, y1, y2; + + /* TODO: hard coding 32 bpp */ + int stride = qfbdev->qfb.base.pitches[0] * 4; + + x1 = qfbdev->dirty.x1; + x2 = qfbdev->dirty.x2; + y1 = qfbdev->dirty.y1; + y2 = qfbdev->dirty.y2; + /* + * we are using a shadow draw buffer, at qdev->surface0_shadow + */ + qxl_io_log(qdev, "dirty x[%d, %d], y[%d, %d]", x1, x2, y1, y2); + image->dx = x1; + image->dy = y1; + image->width = x2 - x1; + image->height = y2 - y1; + image->fg_color = 0xffffffff; /* unused, just to avoid uninitialized + warnings */ + image->bg_color = 0; + image->depth = 32; /* TODO: take from somewhere? */ + image->cmap.start = 0; + image->cmap.len = 0; + image->cmap.red = NULL; + image->cmap.green = NULL; + image->cmap.blue = NULL; + image->cmap.transp = NULL; + image->data = qfbdev->shadow + (x1 * 4) + (stride * y1); + + qxl_fb_image_init(&qxl_fb_image, qdev, info, NULL); + qxl_draw_opaque_fb(&qxl_fb_image, stride); + qfbdev->dirty.x1 = 0; + qfbdev->dirty.x2 = 0; + qfbdev->dirty.y1 = 0; + qfbdev->dirty.y2 = 0; +} + +static void qxl_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct qxl_fbdev *qfbdev = info->par; + unsigned long start, end, min, max; + struct page *page; + int y1, y2; + + min = ULONG_MAX; + max = 0; + list_for_each_entry(page, pagelist, lru) { + start = page->index << PAGE_SHIFT; + end = start + PAGE_SIZE - 1; + min = min(min, start); + max = max(max, end); + } + + if (min < max) { + y1 = min / info->fix.line_length; + y2 = (max / info->fix.line_length) + 1; + + /* TODO: add spin lock? */ + /* spin_lock_irqsave(&qfbdev->dirty.lock, flags); */ + qfbdev->dirty.x1 = 0; + qfbdev->dirty.y1 = y1; + qfbdev->dirty.x2 = info->var.xres; + qfbdev->dirty.y2 = y2; + /* spin_unlock_irqrestore(&qfbdev->dirty.lock, flags); */ + } + + qxl_fb_dirty_flush(info); +}; + + +static struct fb_deferred_io qxl_defio = { + .delay = QXL_DIRTY_DELAY, + .deferred_io = qxl_deferred_io, +}; + +static void qxl_fb_fillrect(struct fb_info *info, + const struct fb_fillrect *fb_rect) +{ + struct qxl_fbdev *qfbdev = info->par; + struct qxl_device *qdev = qfbdev->qdev; + struct qxl_rect rect; + uint32_t color; + int x = fb_rect->dx; + int y = fb_rect->dy; + int width = fb_rect->width; + int height = fb_rect->height; + uint16_t rop; + struct qxl_draw_fill qxl_draw_fill_rec; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) + color = ((u32 *) (info->pseudo_palette))[fb_rect->color]; + else + color = fb_rect->color; + rect.left = x; + rect.right = x + width; + rect.top = y; + rect.bottom = y + height; + switch (fb_rect->rop) { + case ROP_XOR: + rop = SPICE_ROPD_OP_XOR; + break; + case ROP_COPY: + rop = SPICE_ROPD_OP_PUT; + break; + default: + pr_err("qxl_fb_fillrect(): unknown rop, " + "defaulting to SPICE_ROPD_OP_PUT\n"); + rop = SPICE_ROPD_OP_PUT; + } + qxl_draw_fill_rec.qdev = qdev; + qxl_draw_fill_rec.rect = rect; + qxl_draw_fill_rec.color = color; + qxl_draw_fill_rec.rop = rop; + if (!drm_can_sleep()) { + qxl_io_log(qdev, + "%s: TODO use RCU, mysterious locks with spin_lock\n", + __func__); + return; + } + qxl_draw_fill(&qxl_draw_fill_rec); +} + +static void qxl_fb_copyarea(struct fb_info *info, + const struct fb_copyarea *region) +{ + struct qxl_fbdev *qfbdev = info->par; + + qxl_draw_copyarea(qfbdev->qdev, + region->width, region->height, + region->sx, region->sy, + region->dx, region->dy); +} + +static void qxl_fb_imageblit_safe(struct qxl_fb_image *qxl_fb_image) +{ + qxl_draw_opaque_fb(qxl_fb_image, 0); +} + +static void qxl_fb_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct qxl_fbdev *qfbdev = info->par; + struct qxl_device *qdev = qfbdev->qdev; + struct qxl_fb_image qxl_fb_image; + + if (!drm_can_sleep()) { + /* we cannot do any ttm_bo allocation since that will fail on + * ioremap_wc..__get_vm_area_node, so queue the work item + * instead This can happen from printk inside an interrupt + * context, i.e.: smp_apic_timer_interrupt..check_cpu_stall */ + qxl_io_log(qdev, + "%s: TODO use RCU, mysterious locks with spin_lock\n", + __func__); + return; + } + + /* ensure proper order of rendering operations - TODO: must do this + * for everything. */ + qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image); + qxl_fb_imageblit_safe(&qxl_fb_image); +} + +int qxl_fb_init(struct qxl_device *qdev) +{ + return 0; +} + +static struct fb_ops qxlfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */ + .fb_fillrect = qxl_fb_fillrect, + .fb_copyarea = qxl_fb_copyarea, + .fb_imageblit = qxl_fb_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static void qxlfb_destroy_pinned_object(struct drm_gem_object *gobj) +{ + struct qxl_bo *qbo = gem_to_qxl_bo(gobj); + int ret; + + ret = qxl_bo_reserve(qbo, false); + if (likely(ret == 0)) { + qxl_bo_kunmap(qbo); + qxl_bo_unpin(qbo); + qxl_bo_unreserve(qbo); + } + drm_gem_object_unreference_unlocked(gobj); +} + +int qxl_get_handle_for_primary_fb(struct qxl_device *qdev, + struct drm_file *file_priv, + uint32_t *handle) +{ + int r; + struct drm_gem_object *gobj = qdev->fbdev_qfb->obj; + + BUG_ON(!gobj); + /* drm_get_handle_create adds a reference - good */ + r = drm_gem_handle_create(file_priv, gobj, handle); + if (r) + return r; + return 0; +} + +static int qxlfb_create_pinned_object(struct qxl_fbdev *qfbdev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **gobj_p) +{ + struct qxl_device *qdev = qfbdev->qdev; + struct drm_gem_object *gobj = NULL; + struct qxl_bo *qbo = NULL; + int ret; + int aligned_size, size; + int height = mode_cmd->height; + int bpp; + int depth; + + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &bpp, &depth); + + size = mode_cmd->pitches[0] * height; + aligned_size = ALIGN(size, PAGE_SIZE); + /* TODO: unallocate and reallocate surface0 for real. Hack to just + * have a large enough surface0 for 1024x768 Xorg 32bpp mode */ + ret = qxl_gem_object_create(qdev, aligned_size, 0, + QXL_GEM_DOMAIN_SURFACE, + false, /* is discardable */ + false, /* is kernel (false means device) */ + NULL, + &gobj); + if (ret) { + pr_err("failed to allocate framebuffer (%d)\n", + aligned_size); + return -ENOMEM; + } + qbo = gem_to_qxl_bo(gobj); + + qbo->surf.width = mode_cmd->width; + qbo->surf.height = mode_cmd->height; + qbo->surf.stride = mode_cmd->pitches[0]; + qbo->surf.format = SPICE_SURFACE_FMT_32_xRGB; + ret = qxl_bo_reserve(qbo, false); + if (unlikely(ret != 0)) + goto out_unref; + ret = qxl_bo_pin(qbo, QXL_GEM_DOMAIN_SURFACE, NULL); + if (ret) { + qxl_bo_unreserve(qbo); + goto out_unref; + } + ret = qxl_bo_kmap(qbo, NULL); + qxl_bo_unreserve(qbo); /* unreserve, will be mmaped */ + if (ret) + goto out_unref; + + *gobj_p = gobj; + return 0; +out_unref: + qxlfb_destroy_pinned_object(gobj); + *gobj_p = NULL; + return ret; +} + +static int qxlfb_create(struct qxl_fbdev *qfbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct qxl_device *qdev = qfbdev->qdev; + struct fb_info *info; + struct drm_framebuffer *fb = NULL; + struct drm_mode_fb_cmd2 mode_cmd; + struct drm_gem_object *gobj = NULL; + struct qxl_bo *qbo = NULL; + struct device *device = &qdev->pdev->dev; + int ret; + int size; + int bpp = sizes->surface_bpp; + int depth = sizes->surface_depth; + void *shadow; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + + mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 1) / 8), 64); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); + + ret = qxlfb_create_pinned_object(qfbdev, &mode_cmd, &gobj); + qbo = gem_to_qxl_bo(gobj); + QXL_INFO(qdev, "%s: %dx%d %d\n", __func__, mode_cmd.width, + mode_cmd.height, mode_cmd.pitches[0]); + + shadow = vmalloc(mode_cmd.pitches[0] * mode_cmd.height); + /* TODO: what's the usual response to memory allocation errors? */ + BUG_ON(!shadow); + QXL_INFO(qdev, + "surface0 at gpu offset %lld, mmap_offset %lld (virt %p, shadow %p)\n", + qxl_bo_gpu_offset(qbo), + qxl_bo_mmap_offset(qbo), + qbo->kptr, + shadow); + size = mode_cmd.pitches[0] * mode_cmd.height; + + info = framebuffer_alloc(0, device); + if (info == NULL) { + ret = -ENOMEM; + goto out_unref; + } + + info->par = qfbdev; + + qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj); + + fb = &qfbdev->qfb.base; + + /* setup helper with fb data */ + qfbdev->helper.fb = fb; + qfbdev->helper.fbdev = info; + qfbdev->shadow = shadow; + strcpy(info->fix.id, "qxldrmfb"); + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; + info->fbops = &qxlfb_ops; + + /* + * TODO: using gobj->size in various places in this function. Not sure + * what the difference between the different sizes is. + */ + info->fix.smem_start = qdev->vram_base; /* TODO - correct? */ + info->fix.smem_len = gobj->size; + info->screen_base = qfbdev->shadow; + info->screen_size = gobj->size; + + drm_fb_helper_fill_var(info, &qfbdev->helper, sizes->fb_width, + sizes->fb_height); + + /* setup aperture base/size for vesafb takeover */ + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto out_unref; + } + info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base; + info->apertures->ranges[0].size = qdev->vram_size; + + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + + if (info->screen_base == NULL) { + ret = -ENOSPC; + goto out_unref; + } + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unref; + } + + info->fbdefio = &qxl_defio; + fb_deferred_io_init(info); + + qdev->fbdev_info = info; + qdev->fbdev_qfb = &qfbdev->qfb; + DRM_INFO("fb mappable at 0x%lX, size %lu\n", info->fix.smem_start, (unsigned long)info->screen_size); + DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", fb->depth, fb->pitches[0], fb->width, fb->height); + return 0; + +out_unref: + if (qbo) { + ret = qxl_bo_reserve(qbo, false); + if (likely(ret == 0)) { + qxl_bo_kunmap(qbo); + qxl_bo_unpin(qbo); + qxl_bo_unreserve(qbo); + } + } + if (fb && ret) { + drm_gem_object_unreference(gobj); + drm_framebuffer_cleanup(fb); + kfree(fb); + } + drm_gem_object_unreference(gobj); + return ret; +} + +static int qxl_fb_find_or_create_single( + struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct qxl_fbdev *qfbdev = (struct qxl_fbdev *)helper; + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = qxlfb_create(qfbdev, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev) +{ + struct fb_info *info; + struct qxl_framebuffer *qfb = &qfbdev->qfb; + + if (qfbdev->helper.fbdev) { + info = qfbdev->helper.fbdev; + + unregister_framebuffer(info); + framebuffer_release(info); + } + if (qfb->obj) { + qxlfb_destroy_pinned_object(qfb->obj); + qfb->obj = NULL; + } + drm_fb_helper_fini(&qfbdev->helper); + vfree(qfbdev->shadow); + drm_framebuffer_cleanup(&qfb->base); + + return 0; +} + +static struct drm_fb_helper_funcs qxl_fb_helper_funcs = { + /* TODO + .gamma_set = qxl_crtc_fb_gamma_set, + .gamma_get = qxl_crtc_fb_gamma_get, + */ + .fb_probe = qxl_fb_find_or_create_single, +}; + +int qxl_fbdev_init(struct qxl_device *qdev) +{ + struct qxl_fbdev *qfbdev; + int bpp_sel = 32; /* TODO: parameter from somewhere? */ + int ret; + + qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL); + if (!qfbdev) + return -ENOMEM; + + qfbdev->qdev = qdev; + qdev->mode_info.qfbdev = qfbdev; + qfbdev->helper.funcs = &qxl_fb_helper_funcs; + + ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, + 1 /* num_crtc - QXL supports just 1 */, + QXLFB_CONN_LIMIT); + if (ret) { + kfree(qfbdev); + return ret; + } + + drm_fb_helper_single_add_all_connectors(&qfbdev->helper); + drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel); + return 0; +} + +void qxl_fbdev_fini(struct qxl_device *qdev) +{ + if (!qdev->mode_info.qfbdev) + return; + + qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev); + kfree(qdev->mode_info.qfbdev); + qdev->mode_info.qfbdev = NULL; +} + + diff --git a/drivers/gpu/drm/qxl/qxl_fence.c b/drivers/gpu/drm/qxl/qxl_fence.c new file mode 100644 index 0000000..63c6715 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_fence.c @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + + +#include "qxl_drv.h" + +/* QXL fencing- + + When we submit operations to the GPU we pass a release reference to the GPU + with them, the release reference is then added to the release ring when + the GPU is finished with that particular operation and has removed it from + its tree. + + So we have can have multiple outstanding non linear fences per object. + + From a TTM POV we only care if the object has any outstanding releases on + it. + + we wait until all outstanding releases are processeed. + + sync object is just a list of release ids that represent that fence on + that buffer. + + we just add new releases onto the sync object attached to the object. + + This currently uses a radix tree to store the list of release ids. + + For some reason every so often qxl hw fails to release, things go wrong. +*/ + + +int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id) +{ + struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence); + + spin_lock(&bo->tbo.bdev->fence_lock); + radix_tree_insert(&qfence->tree, rel_id, qfence); + qfence->num_active_releases++; + spin_unlock(&bo->tbo.bdev->fence_lock); + return 0; +} + +int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id) +{ + void *ret; + int retval = 0; + struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence); + + spin_lock(&bo->tbo.bdev->fence_lock); + + ret = radix_tree_delete(&qfence->tree, rel_id); + if (ret == qfence) + qfence->num_active_releases--; + else { + DRM_DEBUG("didn't find fence in radix tree for %d\n", rel_id); + retval = -ENOENT; + } + spin_unlock(&bo->tbo.bdev->fence_lock); + return retval; +} + + +int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence) +{ + qfence->qdev = qdev; + qfence->num_active_releases = 0; + INIT_RADIX_TREE(&qfence->tree, GFP_ATOMIC); + return 0; +} + +void qxl_fence_fini(struct qxl_fence *qfence) +{ + kfree(qfence->release_ids); + qfence->num_active_releases = 0; +} diff --git a/drivers/gpu/drm/qxl/qxl_gem.c b/drivers/gpu/drm/qxl/qxl_gem.c new file mode 100644 index 0000000..a235693 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_gem.c @@ -0,0 +1,149 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include "drmP.h" +#include "drm/drm.h" +#include "qxl_drv.h" +#include "qxl_object.h" + +int qxl_gem_object_init(struct drm_gem_object *obj) +{ + /* we do nothings here */ + return 0; +} + +void qxl_gem_object_free(struct drm_gem_object *gobj) +{ + struct qxl_bo *qobj = gem_to_qxl_bo(gobj); + + if (qobj) + qxl_bo_unref(&qobj); +} + +int qxl_gem_object_create(struct qxl_device *qdev, int size, + int alignment, int initial_domain, + bool discardable, bool kernel, + struct qxl_surface *surf, + struct drm_gem_object **obj) +{ + struct qxl_bo *qbo; + int r; + + *obj = NULL; + /* At least align on page size */ + if (alignment < PAGE_SIZE) + alignment = PAGE_SIZE; + r = qxl_bo_create(qdev, size, kernel, initial_domain, surf, &qbo); + if (r) { + if (r != -ERESTARTSYS) + DRM_ERROR( + "Failed to allocate GEM object (%d, %d, %u, %d)\n", + size, initial_domain, alignment, r); + return r; + } + *obj = &qbo->gem_base; + + mutex_lock(&qdev->gem.mutex); + list_add_tail(&qbo->list, &qdev->gem.objects); + mutex_unlock(&qdev->gem.mutex); + + return 0; +} + +int qxl_gem_object_create_with_handle(struct qxl_device *qdev, + struct drm_file *file_priv, + u32 domain, + size_t size, + struct qxl_surface *surf, + struct qxl_bo **qobj, + uint32_t *handle) +{ + struct drm_gem_object *gobj; + int r; + + BUG_ON(!qobj); + BUG_ON(!handle); + + r = qxl_gem_object_create(qdev, size, 0, + domain, + false, false, surf, + &gobj); + if (r) + return -ENOMEM; + r = drm_gem_handle_create(file_priv, gobj, handle); + if (r) + return r; + /* drop reference from allocate - handle holds it now */ + *qobj = gem_to_qxl_bo(gobj); + drm_gem_object_unreference_unlocked(gobj); + return 0; +} + +int qxl_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain, + uint64_t *gpu_addr) +{ + struct qxl_bo *qobj = obj->driver_private; + int r; + + r = qxl_bo_reserve(qobj, false); + if (unlikely(r != 0)) + return r; + r = qxl_bo_pin(qobj, pin_domain, gpu_addr); + qxl_bo_unreserve(qobj); + return r; +} + +void qxl_gem_object_unpin(struct drm_gem_object *obj) +{ + struct qxl_bo *qobj = obj->driver_private; + int r; + + r = qxl_bo_reserve(qobj, false); + if (likely(r == 0)) { + qxl_bo_unpin(qobj); + qxl_bo_unreserve(qobj); + } +} + +int qxl_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv) +{ + return 0; +} + +void qxl_gem_object_close(struct drm_gem_object *obj, + struct drm_file *file_priv) +{ +} + +int qxl_gem_init(struct qxl_device *qdev) +{ + INIT_LIST_HEAD(&qdev->gem.objects); + return 0; +} + +void qxl_gem_fini(struct qxl_device *qdev) +{ + qxl_bo_force_delete(qdev); +} diff --git a/drivers/gpu/drm/qxl/qxl_image.c b/drivers/gpu/drm/qxl/qxl_image.c new file mode 100644 index 0000000..cf85620 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_image.c @@ -0,0 +1,176 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include <linux/gfp.h> +#include <linux/slab.h> + +#include "qxl_drv.h" +#include "qxl_object.h" + +static int +qxl_image_create_helper(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_bo **image_bo, + const uint8_t *data, + int width, int height, + int depth, unsigned int hash, + int stride) +{ + struct qxl_image *image; + struct qxl_data_chunk *chunk; + int i; + int chunk_stride; + int linesize = width * depth / 8; + struct qxl_bo *chunk_bo; + int ret; + void *ptr; + /* Chunk */ + /* FIXME: Check integer overflow */ + /* TODO: variable number of chunks */ + chunk_stride = stride; /* TODO: should use linesize, but it renders + wrong (check the bitmaps are sent correctly + first) */ + ret = qxl_alloc_bo_reserved(qdev, sizeof(*chunk) + height * chunk_stride, + &chunk_bo); + + ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0); + chunk = ptr; + chunk->data_size = height * chunk_stride; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr); + + { + void *k_data, *i_data; + int remain; + int page; + int size; + if (stride == linesize && chunk_stride == stride) { + remain = linesize * height; + page = 0; + i_data = (void *)data; + + while (remain > 0) { + ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT); + + if (page == 0) { + chunk = ptr; + k_data = chunk->data; + size = PAGE_SIZE - offsetof(struct qxl_data_chunk, data); + } else { + k_data = ptr; + size = PAGE_SIZE; + } + size = min(size, remain); + + memcpy(k_data, i_data, size); + + qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr); + i_data += size; + remain -= size; + page++; + } + } else { + unsigned page_base, page_offset, out_offset; + for (i = 0 ; i < height ; ++i) { + i_data = (void *)data + i * stride; + remain = linesize; + out_offset = offsetof(struct qxl_data_chunk, data) + i * chunk_stride; + + while (remain > 0) { + page_base = out_offset & PAGE_MASK; + page_offset = offset_in_page(out_offset); + + size = min((int)(PAGE_SIZE - page_offset), remain); + + ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base); + k_data = ptr + page_offset; + memcpy(k_data, i_data, size); + qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr); + remain -= size; + i_data += size; + out_offset += size; + } + } + } + } + + + qxl_bo_kunmap(chunk_bo); + + /* Image */ + ret = qxl_alloc_bo_reserved(qdev, sizeof(*image), image_bo); + + ptr = qxl_bo_kmap_atomic_page(qdev, *image_bo, 0); + image = ptr; + + image->descriptor.id = 0; + image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; + + image->descriptor.flags = 0; + image->descriptor.width = width; + image->descriptor.height = height; + + switch (depth) { + case 1: + /* TODO: BE? check by arch? */ + image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE; + break; + case 24: + image->u.bitmap.format = SPICE_BITMAP_FMT_24BIT; + break; + case 32: + image->u.bitmap.format = SPICE_BITMAP_FMT_32BIT; + break; + default: + DRM_ERROR("unsupported image bit depth\n"); + return -EINVAL; /* TODO: cleanup */ + } + image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN; + image->u.bitmap.x = width; + image->u.bitmap.y = height; + image->u.bitmap.stride = chunk_stride; + image->u.bitmap.palette = 0; + image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0); + qxl_release_add_res(qdev, release, chunk_bo); + qxl_bo_unreserve(chunk_bo); + qxl_bo_unref(&chunk_bo); + + qxl_bo_kunmap_atomic_page(qdev, *image_bo, ptr); + + return 0; +} + +int qxl_image_create(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_bo **image_bo, + const uint8_t *data, + int x, int y, int width, int height, + int depth, int stride) +{ + data += y * stride + x * (depth / 8); + return qxl_image_create_helper(qdev, release, image_bo, data, + width, height, depth, 0, stride); +} diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c new file mode 100644 index 0000000..04b64f9 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -0,0 +1,411 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include "qxl_drv.h" +#include "qxl_object.h" + +/* + * TODO: allocating a new gem(in qxl_bo) for each request. + * This is wasteful since bo's are page aligned. + */ +static int qxl_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_alloc *qxl_alloc = data; + int ret; + struct qxl_bo *qobj; + uint32_t handle; + u32 domain = QXL_GEM_DOMAIN_VRAM; + + if (qxl_alloc->size == 0) { + DRM_ERROR("invalid size %d\n", qxl_alloc->size); + return -EINVAL; + } + ret = qxl_gem_object_create_with_handle(qdev, file_priv, + domain, + qxl_alloc->size, + NULL, + &qobj, &handle); + if (ret) { + DRM_ERROR("%s: failed to create gem ret=%d\n", + __func__, ret); + return -ENOMEM; + } + qxl_alloc->handle = handle; + return 0; +} + +static int qxl_map_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_map *qxl_map = data; + + return qxl_mode_dumb_mmap(file_priv, qdev->ddev, qxl_map->handle, + &qxl_map->offset); +} + +/* + * dst must be validated, i.e. whole bo on vram/surfacesram (right now all bo's + * are on vram). + * *(dst + dst_off) = qxl_bo_physical_address(src, src_off) + */ +static void +apply_reloc(struct qxl_device *qdev, struct qxl_bo *dst, uint64_t dst_off, + struct qxl_bo *src, uint64_t src_off) +{ + void *reloc_page; + + reloc_page = qxl_bo_kmap_atomic_page(qdev, dst, dst_off & PAGE_MASK); + *(uint64_t *)(reloc_page + (dst_off & ~PAGE_MASK)) = qxl_bo_physical_address(qdev, + src, src_off); + qxl_bo_kunmap_atomic_page(qdev, dst, reloc_page); +} + +static void +apply_surf_reloc(struct qxl_device *qdev, struct qxl_bo *dst, uint64_t dst_off, + struct qxl_bo *src) +{ + uint32_t id = 0; + void *reloc_page; + + if (src && !src->is_primary) + id = src->surface_id; + + reloc_page = qxl_bo_kmap_atomic_page(qdev, dst, dst_off & PAGE_MASK); + *(uint32_t *)(reloc_page + (dst_off & ~PAGE_MASK)) = id; + qxl_bo_kunmap_atomic_page(qdev, dst, reloc_page); +} + +/* return holding the reference to this object */ +static struct qxl_bo *qxlhw_handle_to_bo(struct qxl_device *qdev, + struct drm_file *file_priv, uint64_t handle, + struct qxl_reloc_list *reloc_list) +{ + struct drm_gem_object *gobj; + struct qxl_bo *qobj; + int ret; + + gobj = drm_gem_object_lookup(qdev->ddev, file_priv, handle); + if (!gobj) { + DRM_ERROR("bad bo handle %lld\n", handle); + return NULL; + } + qobj = gem_to_qxl_bo(gobj); + + ret = qxl_bo_list_add(reloc_list, qobj); + if (ret) + return NULL; + + return qobj; +} + +/* + * Usage of execbuffer: + * Relocations need to take into account the full QXLDrawable size. + * However, the command as passed from user space must *not* contain the initial + * QXLReleaseInfo struct (first XXX bytes) + */ +static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_execbuffer *execbuffer = data; + struct drm_qxl_command user_cmd; + int cmd_num; + struct qxl_bo *reloc_src_bo; + struct qxl_bo *reloc_dst_bo; + struct drm_qxl_reloc reloc; + void *fb_cmd; + int i, ret; + struct qxl_reloc_list reloc_list; + int unwritten; + uint32_t reloc_dst_offset; + INIT_LIST_HEAD(&reloc_list.bos); + + for (cmd_num = 0; cmd_num < execbuffer->commands_num; ++cmd_num) { + struct qxl_release *release; + struct qxl_bo *cmd_bo; + int release_type; + struct drm_qxl_command *commands = + (struct drm_qxl_command *)execbuffer->commands; + + if (DRM_COPY_FROM_USER(&user_cmd, &commands[cmd_num], + sizeof(user_cmd))) + return -EFAULT; + switch (user_cmd.type) { + case QXL_CMD_DRAW: + release_type = QXL_RELEASE_DRAWABLE; + break; + case QXL_CMD_SURFACE: + case QXL_CMD_CURSOR: + default: + DRM_DEBUG("Only draw commands in execbuffers\n"); + return -EINVAL; + break; + } + + if (user_cmd.command_size > PAGE_SIZE - sizeof(union qxl_release_info)) + return -EINVAL; + + ret = qxl_alloc_release_reserved(qdev, + sizeof(union qxl_release_info) + + user_cmd.command_size, + release_type, + &release, + &cmd_bo); + if (ret) + return ret; + + /* TODO copy slow path code from i915 */ + fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE)); + unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)user_cmd.command, user_cmd.command_size); + qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd); + if (unwritten) { + DRM_ERROR("got unwritten %d\n", unwritten); + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); + return -EFAULT; + } + + for (i = 0 ; i < user_cmd.relocs_num; ++i) { + if (DRM_COPY_FROM_USER(&reloc, + &((struct drm_qxl_reloc *)user_cmd.relocs)[i], + sizeof(reloc))) { + qxl_bo_list_unreserve(&reloc_list, true); + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); + return -EFAULT; + } + + /* add the bos to the list of bos to validate - + need to validate first then process relocs? */ + if (reloc.dst_handle) { + reloc_dst_bo = qxlhw_handle_to_bo(qdev, file_priv, + reloc.dst_handle, &reloc_list); + if (!reloc_dst_bo) { + qxl_bo_list_unreserve(&reloc_list, true); + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); + return -EINVAL; + } + reloc_dst_offset = 0; + } else { + reloc_dst_bo = cmd_bo; + reloc_dst_offset = release->release_offset; + } + + /* reserve and validate the reloc dst bo */ + if (reloc.reloc_type == QXL_RELOC_TYPE_BO || reloc.src_handle > 0) { + reloc_src_bo = + qxlhw_handle_to_bo(qdev, file_priv, + reloc.src_handle, &reloc_list); + if (!reloc_src_bo) { + if (reloc_dst_bo != cmd_bo) + drm_gem_object_unreference_unlocked(&reloc_dst_bo->gem_base); + qxl_bo_list_unreserve(&reloc_list, true); + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); + return -EINVAL; + } + } else + reloc_src_bo = NULL; + if (reloc.reloc_type == QXL_RELOC_TYPE_BO) { + apply_reloc(qdev, reloc_dst_bo, reloc_dst_offset + reloc.dst_offset, + reloc_src_bo, reloc.src_offset); + } else if (reloc.reloc_type == QXL_RELOC_TYPE_SURF) { + apply_surf_reloc(qdev, reloc_dst_bo, reloc_dst_offset + reloc.dst_offset, reloc_src_bo); + } else { + DRM_ERROR("unknown reloc type %d\n", reloc.reloc_type); + return -EINVAL; + } + + if (reloc_src_bo && reloc_src_bo != cmd_bo) { + qxl_release_add_res(qdev, release, reloc_src_bo); + drm_gem_object_unreference_unlocked(&reloc_src_bo->gem_base); + } + + if (reloc_dst_bo != cmd_bo) + drm_gem_object_unreference_unlocked(&reloc_dst_bo->gem_base); + } + qxl_fence_releaseable(qdev, release); + + ret = qxl_push_command_ring_release(qdev, release, user_cmd.type, true); + if (ret == -ERESTARTSYS) { + qxl_release_unreserve(qdev, release); + qxl_release_free(qdev, release); + qxl_bo_list_unreserve(&reloc_list, true); + return ret; + } + qxl_release_unreserve(qdev, release); + } + qxl_bo_list_unreserve(&reloc_list, 0); + return 0; +} + +static int qxl_update_area_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_update_area *update_area = data; + struct qxl_rect area = {.left = update_area->left, + .top = update_area->top, + .right = update_area->right, + .bottom = update_area->bottom}; + int ret; + struct drm_gem_object *gobj = NULL; + struct qxl_bo *qobj = NULL; + + if (update_area->left >= update_area->right || + update_area->top >= update_area->bottom) + return -EINVAL; + + gobj = drm_gem_object_lookup(dev, file, update_area->handle); + if (gobj == NULL) + return -ENOENT; + + qobj = gem_to_qxl_bo(gobj); + + ret = qxl_bo_reserve(qobj, false); + if (ret) + goto out; + + if (!qobj->pin_count) { + ret = ttm_bo_validate(&qobj->tbo, &qobj->placement, + true, false); + if (unlikely(ret)) + goto out; + } + + ret = qxl_bo_check_id(qdev, qobj); + if (ret) + goto out2; + if (!qobj->surface_id) + DRM_ERROR("got update area for surface with no id %d\n", update_area->handle); + ret = qxl_io_update_area(qdev, qobj, &area); + +out2: + qxl_bo_unreserve(qobj); + +out: + drm_gem_object_unreference_unlocked(gobj); + return ret; +} + +static int qxl_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_getparam *param = data; + + switch (param->param) { + case QXL_PARAM_NUM_SURFACES: + param->value = qdev->rom->n_surfaces; + break; + case QXL_PARAM_MAX_RELOCS: + param->value = QXL_MAX_RES; + break; + default: + return -EINVAL; + } + return 0; +} + +static int qxl_clientcap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_clientcap *param = data; + int byte, idx; + + byte = param->index / 8; + idx = param->index % 8; + + if (qdev->pdev->revision < 4) + return -ENOSYS; + + if (byte >= 58) + return -ENOSYS; + + if (qdev->rom->client_capabilities[byte] & (1 << idx)) + return 0; + return -ENOSYS; +} + +static int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_alloc_surf *param = data; + struct qxl_bo *qobj; + int handle; + int ret; + int size, actual_stride; + struct qxl_surface surf; + + /* work out size allocate bo with handle */ + actual_stride = param->stride < 0 ? -param->stride : param->stride; + size = actual_stride * param->height + actual_stride; + + surf.format = param->format; + surf.width = param->width; + surf.height = param->height; + surf.stride = param->stride; + surf.data = 0; + + ret = qxl_gem_object_create_with_handle(qdev, file, + QXL_GEM_DOMAIN_SURFACE, + size, + &surf, + &qobj, &handle); + if (ret) { + DRM_ERROR("%s: failed to create gem ret=%d\n", + __func__, ret); + return -ENOMEM; + } else + param->handle = handle; + return ret; +} + +struct drm_ioctl_desc qxl_ioctls[] = { + DRM_IOCTL_DEF_DRV(QXL_ALLOC, qxl_alloc_ioctl, DRM_AUTH|DRM_UNLOCKED), + + DRM_IOCTL_DEF_DRV(QXL_MAP, qxl_map_ioctl, DRM_AUTH|DRM_UNLOCKED), + + DRM_IOCTL_DEF_DRV(QXL_EXECBUFFER, qxl_execbuffer_ioctl, + DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(QXL_UPDATE_AREA, qxl_update_area_ioctl, + DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(QXL_GETPARAM, qxl_getparam_ioctl, + DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(QXL_CLIENTCAP, qxl_clientcap_ioctl, + DRM_AUTH|DRM_UNLOCKED), + + DRM_IOCTL_DEF_DRV(QXL_ALLOC_SURF, qxl_alloc_surf_ioctl, + DRM_AUTH|DRM_UNLOCKED), +}; + +int qxl_max_ioctls = DRM_ARRAY_SIZE(qxl_ioctls); diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c new file mode 100644 index 0000000..21393dc --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_irq.c @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include "qxl_drv.h" + +irqreturn_t qxl_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + struct qxl_device *qdev = (struct qxl_device *)dev->dev_private; + uint32_t pending; + + pending = xchg(&qdev->ram_header->int_pending, 0); + + atomic_inc(&qdev->irq_received); + + if (pending & QXL_INTERRUPT_DISPLAY) { + atomic_inc(&qdev->irq_received_display); + wake_up_all(&qdev->display_event); + qxl_queue_garbage_collect(qdev, false); + } + if (pending & QXL_INTERRUPT_CURSOR) { + atomic_inc(&qdev->irq_received_cursor); + wake_up_all(&qdev->cursor_event); + } + if (pending & QXL_INTERRUPT_IO_CMD) { + atomic_inc(&qdev->irq_received_io_cmd); + wake_up_all(&qdev->io_cmd_event); + } + if (pending & QXL_INTERRUPT_ERROR) { + /* TODO: log it, reset device (only way to exit this condition) + * (do it a certain number of times, afterwards admit defeat, + * to avoid endless loops). + */ + qdev->irq_received_error++; + qxl_io_log(qdev, "%s: driver is in bug mode.\n", __func__); + } + if (pending & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG) { + qxl_io_log(qdev, "QXL_INTERRUPT_CLIENT_MONITORS_CONFIG\n"); + schedule_work(&qdev->client_monitors_config_work); + } + qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; + outb(0, qdev->io_base + QXL_IO_UPDATE_IRQ); + return IRQ_HANDLED; +} + +static void qxl_client_monitors_config_work_func(struct work_struct *work) +{ + struct qxl_device *qdev = container_of(work, struct qxl_device, + client_monitors_config_work); + + qxl_display_read_client_monitors_config(qdev); +} + +int qxl_irq_init(struct qxl_device *qdev) +{ + int ret; + + init_waitqueue_head(&qdev->display_event); + init_waitqueue_head(&qdev->cursor_event); + init_waitqueue_head(&qdev->io_cmd_event); + INIT_WORK(&qdev->client_monitors_config_work, + qxl_client_monitors_config_work_func); + atomic_set(&qdev->irq_received, 0); + atomic_set(&qdev->irq_received_display, 0); + atomic_set(&qdev->irq_received_cursor, 0); + atomic_set(&qdev->irq_received_io_cmd, 0); + qdev->irq_received_error = 0; + ret = drm_irq_install(qdev->ddev); + qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; + if (unlikely(ret != 0)) { + DRM_ERROR("Failed installing irq: %d\n", ret); + return 1; + } + return 0; +} diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c new file mode 100644 index 0000000..85127ed --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -0,0 +1,302 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include "qxl_drv.h" +#include "qxl_object.h" + +#include <linux/io-mapping.h> + +int qxl_log_level; + +static void qxl_dump_mode(struct qxl_device *qdev, void *p) +{ + struct qxl_mode *m = p; + DRM_DEBUG_KMS("%d: %dx%d %d bits, stride %d, %dmm x %dmm, orientation %d\n", + m->id, m->x_res, m->y_res, m->bits, m->stride, m->x_mili, + m->y_mili, m->orientation); +} + +static bool qxl_check_device(struct qxl_device *qdev) +{ + struct qxl_rom *rom = qdev->rom; + int mode_offset; + int i; + + if (rom->magic != 0x4f525851) { + DRM_ERROR("bad rom signature %x\n", rom->magic); + return false; + } + + DRM_INFO("Device Version %d.%d\n", rom->id, rom->update_id); + DRM_INFO("Compression level %d log level %d\n", rom->compression_level, + rom->log_level); + DRM_INFO("Currently using mode #%d, list at 0x%x\n", + rom->mode, rom->modes_offset); + DRM_INFO("%d io pages at offset 0x%x\n", + rom->num_io_pages, rom->pages_offset); + DRM_INFO("%d byte draw area at offset 0x%x\n", + rom->surface0_area_size, rom->draw_area_offset); + + qdev->vram_size = rom->surface0_area_size; + DRM_INFO("RAM header offset: 0x%x\n", rom->ram_header_offset); + + mode_offset = rom->modes_offset / 4; + qdev->mode_info.num_modes = ((u32 *)rom)[mode_offset]; + DRM_INFO("rom modes offset 0x%x for %d modes\n", rom->modes_offset, + qdev->mode_info.num_modes); + qdev->mode_info.modes = (void *)((uint32_t *)rom + mode_offset + 1); + for (i = 0; i < qdev->mode_info.num_modes; i++) + qxl_dump_mode(qdev, qdev->mode_info.modes + i); + return true; +} + +static uint8_t setup_slot(struct qxl_device *qdev, uint8_t slot_index_offset, + unsigned long start_phys_addr, unsigned long end_phys_addr) +{ + uint64_t high_bits; + struct qxl_memslot *slot; + uint8_t slot_index; + struct qxl_ram_header *ram_header = qdev->ram_header; + + slot_index = qdev->rom->slots_start + slot_index_offset; + slot = &qdev->mem_slots[slot_index]; + slot->start_phys_addr = start_phys_addr; + slot->end_phys_addr = end_phys_addr; + ram_header->mem_slot.mem_start = slot->start_phys_addr; + ram_header->mem_slot.mem_end = slot->end_phys_addr; + qxl_io_memslot_add(qdev, slot_index); + slot->generation = qdev->rom->slot_generation; + high_bits = slot_index << qdev->slot_gen_bits; + high_bits |= slot->generation; + high_bits <<= (64 - (qdev->slot_gen_bits + qdev->slot_id_bits)); + slot->high_bits = high_bits; + return slot_index; +} + +static void qxl_gc_work(struct work_struct *work) +{ + struct qxl_device *qdev = container_of(work, struct qxl_device, gc_work); + qxl_garbage_collect(qdev); +} + +int qxl_device_init(struct qxl_device *qdev, + struct drm_device *ddev, + struct pci_dev *pdev, + unsigned long flags) +{ + int r; + + qdev->dev = &pdev->dev; + qdev->ddev = ddev; + qdev->pdev = pdev; + qdev->flags = flags; + + mutex_init(&qdev->gem.mutex); + mutex_init(&qdev->update_area_mutex); + mutex_init(&qdev->release_mutex); + mutex_init(&qdev->surf_evict_mutex); + INIT_LIST_HEAD(&qdev->gem.objects); + + qdev->rom_base = pci_resource_start(pdev, 2); + qdev->rom_size = pci_resource_len(pdev, 2); + qdev->vram_base = pci_resource_start(pdev, 0); + qdev->surfaceram_base = pci_resource_start(pdev, 1); + qdev->surfaceram_size = pci_resource_len(pdev, 1); + qdev->io_base = pci_resource_start(pdev, 3); + + qdev->vram_mapping = io_mapping_create_wc(qdev->vram_base, pci_resource_len(pdev, 0)); + qdev->surface_mapping = io_mapping_create_wc(qdev->surfaceram_base, qdev->surfaceram_size); + DRM_DEBUG_KMS("qxl: vram %p-%p(%dM %dk), surface %p-%p(%dM %dk)\n", + (void *)qdev->vram_base, (void *)pci_resource_end(pdev, 0), + (int)pci_resource_len(pdev, 0) / 1024 / 1024, + (int)pci_resource_len(pdev, 0) / 1024, + (void *)qdev->surfaceram_base, + (void *)pci_resource_end(pdev, 1), + (int)qdev->surfaceram_size / 1024 / 1024, + (int)qdev->surfaceram_size / 1024); + + qdev->rom = ioremap(qdev->rom_base, qdev->rom_size); + if (!qdev->rom) { + pr_err("Unable to ioremap ROM\n"); + return -ENOMEM; + } + + qxl_check_device(qdev); + + r = qxl_bo_init(qdev); + if (r) { + DRM_ERROR("bo init failed %d\n", r); + return r; + } + + qdev->ram_header = ioremap(qdev->vram_base + + qdev->rom->ram_header_offset, + sizeof(*qdev->ram_header)); + + qdev->command_ring = qxl_ring_create(&(qdev->ram_header->cmd_ring_hdr), + sizeof(struct qxl_command), + QXL_COMMAND_RING_SIZE, + qdev->io_base + QXL_IO_NOTIFY_CMD, + false, + &qdev->display_event); + + qdev->cursor_ring = qxl_ring_create( + &(qdev->ram_header->cursor_ring_hdr), + sizeof(struct qxl_command), + QXL_CURSOR_RING_SIZE, + qdev->io_base + QXL_IO_NOTIFY_CMD, + false, + &qdev->cursor_event); + + qdev->release_ring = qxl_ring_create( + &(qdev->ram_header->release_ring_hdr), + sizeof(uint64_t), + QXL_RELEASE_RING_SIZE, 0, true, + NULL); + + /* TODO - slot initialization should happen on reset. where is our + * reset handler? */ + qdev->n_mem_slots = qdev->rom->slots_end; + qdev->slot_gen_bits = qdev->rom->slot_gen_bits; + qdev->slot_id_bits = qdev->rom->slot_id_bits; + qdev->va_slot_mask = + (~(uint64_t)0) >> (qdev->slot_id_bits + qdev->slot_gen_bits); + + qdev->mem_slots = + kmalloc(qdev->n_mem_slots * sizeof(struct qxl_memslot), + GFP_KERNEL); + + idr_init(&qdev->release_idr); + spin_lock_init(&qdev->release_idr_lock); + + idr_init(&qdev->surf_id_idr); + spin_lock_init(&qdev->surf_id_idr_lock); + + mutex_init(&qdev->async_io_mutex); + + /* reset the device into a known state - no memslots, no primary + * created, no surfaces. */ + qxl_io_reset(qdev); + + /* must initialize irq before first async io - slot creation */ + r = qxl_irq_init(qdev); + if (r) + return r; + + /* + * Note that virtual is surface0. We rely on the single ioremap done + * before. + */ + qdev->main_mem_slot = setup_slot(qdev, 0, + (unsigned long)qdev->vram_base, + (unsigned long)qdev->vram_base + qdev->rom->ram_header_offset); + qdev->surfaces_mem_slot = setup_slot(qdev, 1, + (unsigned long)qdev->surfaceram_base, + (unsigned long)qdev->surfaceram_base + qdev->surfaceram_size); + DRM_INFO("main mem slot %d [%lx,%x)\n", + qdev->main_mem_slot, + (unsigned long)qdev->vram_base, qdev->rom->ram_header_offset); + + + qdev->gc_queue = create_singlethread_workqueue("qxl_gc"); + INIT_WORK(&qdev->gc_work, qxl_gc_work); + + r = qxl_fb_init(qdev); + if (r) + return r; + + return 0; +} + +static void qxl_device_fini(struct qxl_device *qdev) +{ + if (qdev->current_release_bo[0]) + qxl_bo_unref(&qdev->current_release_bo[0]); + if (qdev->current_release_bo[1]) + qxl_bo_unref(&qdev->current_release_bo[1]); + flush_workqueue(qdev->gc_queue); + destroy_workqueue(qdev->gc_queue); + qdev->gc_queue = NULL; + + qxl_ring_free(qdev->command_ring); + qxl_ring_free(qdev->cursor_ring); + qxl_ring_free(qdev->release_ring); + qxl_bo_fini(qdev); + io_mapping_free(qdev->surface_mapping); + io_mapping_free(qdev->vram_mapping); + iounmap(qdev->ram_header); + iounmap(qdev->rom); + qdev->rom = NULL; + qdev->mode_info.modes = NULL; + qdev->mode_info.num_modes = 0; + qxl_debugfs_remove_files(qdev); +} + +int qxl_driver_unload(struct drm_device *dev) +{ + struct qxl_device *qdev = dev->dev_private; + + if (qdev == NULL) + return 0; + qxl_modeset_fini(qdev); + qxl_device_fini(qdev); + + kfree(qdev); + dev->dev_private = NULL; + return 0; +} + +int qxl_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct qxl_device *qdev; + int r; + + /* require kms */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL); + if (qdev == NULL) + return -ENOMEM; + + dev->dev_private = qdev; + + r = qxl_device_init(qdev, dev, dev->pdev, flags); + if (r) + goto out; + + r = qxl_modeset_init(qdev); + if (r) { + qxl_driver_unload(dev); + goto out; + } + + return 0; +out: + kfree(qdev); + return r; +} + + diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c new file mode 100644 index 0000000..d9b12e7 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -0,0 +1,365 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include "qxl_drv.h" +#include "qxl_object.h" + +#include <linux/io-mapping.h> +static void qxl_ttm_bo_destroy(struct ttm_buffer_object *tbo) +{ + struct qxl_bo *bo; + struct qxl_device *qdev; + + bo = container_of(tbo, struct qxl_bo, tbo); + qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + + qxl_surface_evict(qdev, bo, false); + qxl_fence_fini(&bo->fence); + mutex_lock(&qdev->gem.mutex); + list_del_init(&bo->list); + mutex_unlock(&qdev->gem.mutex); + drm_gem_object_release(&bo->gem_base); + kfree(bo); +} + +bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo) +{ + if (bo->destroy == &qxl_ttm_bo_destroy) + return true; + return false; +} + +void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain) +{ + u32 c = 0; + + qbo->placement.fpfn = 0; + qbo->placement.lpfn = 0; + qbo->placement.placement = qbo->placements; + qbo->placement.busy_placement = qbo->placements; + if (domain == QXL_GEM_DOMAIN_VRAM) + qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM; + if (domain == QXL_GEM_DOMAIN_SURFACE) + qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0; + if (domain == QXL_GEM_DOMAIN_CPU) + qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + if (!c) + qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + qbo->placement.num_placement = c; + qbo->placement.num_busy_placement = c; +} + + +int qxl_bo_create(struct qxl_device *qdev, + unsigned long size, bool kernel, u32 domain, + struct qxl_surface *surf, + struct qxl_bo **bo_ptr) +{ + struct qxl_bo *bo; + enum ttm_bo_type type; + int r; + + if (unlikely(qdev->mman.bdev.dev_mapping == NULL)) + qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping; + if (kernel) + type = ttm_bo_type_kernel; + else + type = ttm_bo_type_device; + *bo_ptr = NULL; + bo = kzalloc(sizeof(struct qxl_bo), GFP_KERNEL); + if (bo == NULL) + return -ENOMEM; + size = roundup(size, PAGE_SIZE); + r = drm_gem_object_init(qdev->ddev, &bo->gem_base, size); + if (unlikely(r)) { + kfree(bo); + return r; + } + bo->gem_base.driver_private = NULL; + bo->type = domain; + bo->pin_count = 0; + bo->surface_id = 0; + qxl_fence_init(qdev, &bo->fence); + INIT_LIST_HEAD(&bo->list); + atomic_set(&bo->reserve_count, 0); + if (surf) + bo->surf = *surf; + + qxl_ttm_placement_from_domain(bo, domain); + + r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type, + &bo->placement, 0, !kernel, NULL, size, + NULL, &qxl_ttm_bo_destroy); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) + dev_err(qdev->dev, + "object_init failed for (%lu, 0x%08X)\n", + size, domain); + return r; + } + *bo_ptr = bo; + return 0; +} + +int qxl_bo_kmap(struct qxl_bo *bo, void **ptr) +{ + bool is_iomem; + int r; + + if (bo->kptr) { + if (ptr) + *ptr = bo->kptr; + return 0; + } + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); + if (r) + return r; + bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); + if (ptr) + *ptr = bo->kptr; + return 0; +} + +void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev, + struct qxl_bo *bo, int page_offset) +{ + struct ttm_mem_type_manager *man = &bo->tbo.bdev->man[bo->tbo.mem.mem_type]; + void *rptr; + int ret; + struct io_mapping *map; + + if (bo->tbo.mem.mem_type == TTM_PL_VRAM) + map = qdev->vram_mapping; + else if (bo->tbo.mem.mem_type == TTM_PL_PRIV0) + map = qdev->surface_mapping; + else + goto fallback; + + (void) ttm_mem_io_lock(man, false); + ret = ttm_mem_io_reserve(bo->tbo.bdev, &bo->tbo.mem); + ttm_mem_io_unlock(man); + + return io_mapping_map_atomic_wc(map, bo->tbo.mem.bus.offset + page_offset); +fallback: + if (bo->kptr) { + rptr = bo->kptr + (page_offset * PAGE_SIZE); + return rptr; + } + + ret = qxl_bo_kmap(bo, &rptr); + if (ret) + return NULL; + + rptr += page_offset * PAGE_SIZE; + return rptr; +} + +void qxl_bo_kunmap(struct qxl_bo *bo) +{ + if (bo->kptr == NULL) + return; + bo->kptr = NULL; + ttm_bo_kunmap(&bo->kmap); +} + +void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, + struct qxl_bo *bo, void *pmap) +{ + struct ttm_mem_type_manager *man = &bo->tbo.bdev->man[bo->tbo.mem.mem_type]; + struct io_mapping *map; + + if (bo->tbo.mem.mem_type == TTM_PL_VRAM) + map = qdev->vram_mapping; + else if (bo->tbo.mem.mem_type == TTM_PL_PRIV0) + map = qdev->surface_mapping; + else + goto fallback; + + io_mapping_unmap_atomic(pmap); + + (void) ttm_mem_io_lock(man, false); + ttm_mem_io_free(bo->tbo.bdev, &bo->tbo.mem); + ttm_mem_io_unlock(man); + return ; + fallback: + qxl_bo_kunmap(bo); +} + +void qxl_bo_unref(struct qxl_bo **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + tbo = &((*bo)->tbo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; +} + +struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) +{ + ttm_bo_reference(&bo->tbo); + return bo; +} + +int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) +{ + struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + int r, i; + + if (bo->pin_count) { + bo->pin_count++; + if (gpu_addr) + *gpu_addr = qxl_bo_gpu_offset(bo); + return 0; + } + qxl_ttm_placement_from_domain(bo, domain); + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); + if (likely(r == 0)) { + bo->pin_count = 1; + if (gpu_addr != NULL) + *gpu_addr = qxl_bo_gpu_offset(bo); + } + if (unlikely(r != 0)) + dev_err(qdev->dev, "%p pin failed\n", bo); + return r; +} + +int qxl_bo_unpin(struct qxl_bo *bo) +{ + struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + int r, i; + + if (!bo->pin_count) { + dev_warn(qdev->dev, "%p unpin not necessary\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); + if (unlikely(r != 0)) + dev_err(qdev->dev, "%p validate failed for unpin\n", bo); + return r; +} + +void qxl_bo_force_delete(struct qxl_device *qdev) +{ + struct qxl_bo *bo, *n; + + if (list_empty(&qdev->gem.objects)) + return; + dev_err(qdev->dev, "Userspace still has active objects !\n"); + list_for_each_entry_safe(bo, n, &qdev->gem.objects, list) { + mutex_lock(&qdev->ddev->struct_mutex); + dev_err(qdev->dev, "%p %p %lu %lu force free\n", + &bo->gem_base, bo, (unsigned long)bo->gem_base.size, + *((unsigned long *)&bo->gem_base.refcount)); + mutex_lock(&qdev->gem.mutex); + list_del_init(&bo->list); + mutex_unlock(&qdev->gem.mutex); + /* this should unref the ttm bo */ + drm_gem_object_unreference(&bo->gem_base); + mutex_unlock(&qdev->ddev->struct_mutex); + } +} + +int qxl_bo_init(struct qxl_device *qdev) +{ + return qxl_ttm_init(qdev); +} + +void qxl_bo_fini(struct qxl_device *qdev) +{ + qxl_ttm_fini(qdev); +} + +int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo) +{ + int ret; + if (bo->type == QXL_GEM_DOMAIN_SURFACE && bo->surface_id == 0) { + /* allocate a surface id for this surface now */ + ret = qxl_surface_id_alloc(qdev, bo); + if (ret) + return ret; + + ret = qxl_hw_surface_alloc(qdev, bo, NULL); + if (ret) + return ret; + } + return 0; +} + +void qxl_bo_list_unreserve(struct qxl_reloc_list *reloc_list, bool failed) +{ + struct qxl_bo_list *entry, *sf; + + list_for_each_entry_safe(entry, sf, &reloc_list->bos, lhead) { + qxl_bo_unreserve(entry->bo); + list_del(&entry->lhead); + kfree(entry); + } +} + +int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo) +{ + struct qxl_bo_list *entry; + int ret; + + list_for_each_entry(entry, &reloc_list->bos, lhead) { + if (entry->bo == bo) + return 0; + } + + entry = kmalloc(sizeof(struct qxl_bo_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->bo = bo; + list_add(&entry->lhead, &reloc_list->bos); + + ret = qxl_bo_reserve(bo, false); + if (ret) + return ret; + + if (!bo->pin_count) { + qxl_ttm_placement_from_domain(bo, bo->type); + ret = ttm_bo_validate(&bo->tbo, &bo->placement, + true, false); + if (ret) + return ret; + } + + /* allocate a surface for reserved + validated buffers */ + ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo); + if (ret) + return ret; + return 0; +} diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h new file mode 100644 index 0000000..b4fd89f --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -0,0 +1,112 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ +#ifndef QXL_OBJECT_H +#define QXL_OBJECT_H + +#include "qxl_drv.h" + +static inline int qxl_bo_reserve(struct qxl_bo *bo, bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) { + struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + dev_err(qdev->dev, "%p reserve failed\n", bo); + } + return r; + } + return 0; +} + +static inline void qxl_bo_unreserve(struct qxl_bo *bo) +{ + ttm_bo_unreserve(&bo->tbo); +} + +static inline u64 qxl_bo_gpu_offset(struct qxl_bo *bo) +{ + return bo->tbo.offset; +} + +static inline unsigned long qxl_bo_size(struct qxl_bo *bo) +{ + return bo->tbo.num_pages << PAGE_SHIFT; +} + +static inline bool qxl_bo_is_reserved(struct qxl_bo *bo) +{ + return !!atomic_read(&bo->tbo.reserved); +} + +static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo) +{ + return bo->tbo.addr_space_offset; +} + +static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, + bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) { + struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + dev_err(qdev->dev, "%p reserve failed for wait\n", + bo); + } + return r; + } + spin_lock(&bo->tbo.bdev->fence_lock); + if (mem_type) + *mem_type = bo->tbo.mem.mem_type; + if (bo->tbo.sync_obj) + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); + spin_unlock(&bo->tbo.bdev->fence_lock); + ttm_bo_unreserve(&bo->tbo); + return r; +} + +extern int qxl_bo_create(struct qxl_device *qdev, + unsigned long size, + bool kernel, u32 domain, + struct qxl_surface *surf, + struct qxl_bo **bo_ptr); +extern int qxl_bo_kmap(struct qxl_bo *bo, void **ptr); +extern void qxl_bo_kunmap(struct qxl_bo *bo); +void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, int page_offset); +void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, void *map); +extern struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo); +extern void qxl_bo_unref(struct qxl_bo **bo); +extern int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr); +extern int qxl_bo_unpin(struct qxl_bo *bo); +extern void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain); +extern bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo); + +extern int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo); +extern void qxl_bo_list_unreserve(struct qxl_reloc_list *reloc_list, bool failed); +#endif diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c new file mode 100644 index 0000000..c4267c7 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -0,0 +1,307 @@ +/* + * Copyright 2011 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "qxl_drv.h" +#include "qxl_object.h" + +/* + * drawable cmd cache - allocate a bunch of VRAM pages, suballocate + * into 256 byte chunks for now - gives 16 cmds per page. + * + * use an ida to index into the chunks? + */ +/* manage releaseables */ +/* stack them 16 high for now -drawable object is 191 */ +#define RELEASE_SIZE 256 +#define RELEASES_PER_BO (4096 / RELEASE_SIZE) +/* put an alloc/dealloc surface cmd into one bo and round up to 128 */ +#define SURFACE_RELEASE_SIZE 128 +#define SURFACE_RELEASES_PER_BO (4096 / SURFACE_RELEASE_SIZE) + +static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE }; +static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO }; +uint64_t +qxl_release_alloc(struct qxl_device *qdev, int type, + struct qxl_release **ret) +{ + struct qxl_release *release; + int handle = 0; + size_t size = sizeof(*release); + int idr_ret; + + release = kmalloc(size, GFP_KERNEL); + if (!release) { + DRM_ERROR("Out of memory\n"); + return 0; + } + release->type = type; + release->bo_count = 0; + release->release_offset = 0; + release->surface_release_id = 0; +again: + if (idr_pre_get(&qdev->release_idr, GFP_KERNEL) == 0) { + DRM_ERROR("Out of memory for release idr\n"); + kfree(release); + goto release_fail; + } + spin_lock(&qdev->release_idr_lock); + idr_ret = idr_get_new_above(&qdev->release_idr, release, 1, &handle); + spin_unlock(&qdev->release_idr_lock); + if (idr_ret == -EAGAIN) + goto again; + if (ret) + *ret = release; + QXL_INFO(qdev, "allocated release %lld\n", handle); + release->id = handle; +release_fail: + + return handle; +} + +void +qxl_release_free(struct qxl_device *qdev, + struct qxl_release *release) +{ + int i; + + QXL_INFO(qdev, "release %d, type %d, %d bos\n", release->id, + release->type, release->bo_count); + + if (release->surface_release_id) + qxl_surface_id_dealloc(qdev, release->surface_release_id); + + for (i = 0 ; i < release->bo_count; ++i) { + QXL_INFO(qdev, "release %llx\n", + release->bos[i]->tbo.addr_space_offset + - DRM_FILE_OFFSET); + qxl_fence_remove_release(&release->bos[i]->fence, release->id); + qxl_bo_unref(&release->bos[i]); + } + spin_lock(&qdev->release_idr_lock); + idr_remove(&qdev->release_idr, release->id); + spin_unlock(&qdev->release_idr_lock); + kfree(release); +} + +void +qxl_release_add_res(struct qxl_device *qdev, struct qxl_release *release, + struct qxl_bo *bo) +{ + int i; + for (i = 0; i < release->bo_count; i++) + if (release->bos[i] == bo) + return; + + if (release->bo_count >= QXL_MAX_RES) { + DRM_ERROR("exceeded max resource on a qxl_release item\n"); + return; + } + release->bos[release->bo_count++] = qxl_bo_ref(bo); +} + +static int qxl_release_bo_alloc(struct qxl_device *qdev, + struct qxl_bo **bo) +{ + int ret; + ret = qxl_bo_create(qdev, PAGE_SIZE, false, QXL_GEM_DOMAIN_VRAM, NULL, + bo); + return ret; +} + +int qxl_release_reserve(struct qxl_device *qdev, + struct qxl_release *release, bool no_wait) +{ + int ret; + if (atomic_inc_return(&release->bos[0]->reserve_count) == 1) { + ret = qxl_bo_reserve(release->bos[0], no_wait); + if (ret) + return ret; + } + return 0; +} + +void qxl_release_unreserve(struct qxl_device *qdev, + struct qxl_release *release) +{ + if (atomic_dec_and_test(&release->bos[0]->reserve_count)) + qxl_bo_unreserve(release->bos[0]); +} + +int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, + enum qxl_surface_cmd_type surface_cmd_type, + struct qxl_release *create_rel, + struct qxl_release **release) +{ + int ret; + + if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) { + int idr_ret; + struct qxl_bo *bo; + union qxl_release_info *info; + + /* stash the release after the create command */ + idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release); + bo = qxl_bo_ref(create_rel->bos[0]); + + (*release)->release_offset = create_rel->release_offset + 64; + + qxl_release_add_res(qdev, *release, bo); + + ret = qxl_release_reserve(qdev, *release, false); + if (ret) { + DRM_ERROR("release reserve failed\n"); + goto out_unref; + } + info = qxl_release_map(qdev, *release); + info->id = idr_ret; + qxl_release_unmap(qdev, *release, info); + + +out_unref: + qxl_bo_unref(&bo); + return ret; + } + + return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_surface_cmd), + QXL_RELEASE_SURFACE_CMD, release, NULL); +} + +int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, + int type, struct qxl_release **release, + struct qxl_bo **rbo) +{ + struct qxl_bo *bo; + int idr_ret; + int ret; + union qxl_release_info *info; + int cur_idx; + + if (type == QXL_RELEASE_DRAWABLE) + cur_idx = 0; + else if (type == QXL_RELEASE_SURFACE_CMD) + cur_idx = 1; + else if (type == QXL_RELEASE_CURSOR_CMD) + cur_idx = 2; + else { + DRM_ERROR("got illegal type: %d\n", type); + return -EINVAL; + } + + idr_ret = qxl_release_alloc(qdev, type, release); + + mutex_lock(&qdev->release_mutex); + if (qdev->current_release_bo_offset[cur_idx] + 1 >= releases_per_bo[cur_idx]) { + qxl_bo_unref(&qdev->current_release_bo[cur_idx]); + qdev->current_release_bo_offset[cur_idx] = 0; + qdev->current_release_bo[cur_idx] = NULL; + } + if (!qdev->current_release_bo[cur_idx]) { + ret = qxl_release_bo_alloc(qdev, &qdev->current_release_bo[cur_idx]); + if (ret) { + mutex_unlock(&qdev->release_mutex); + return ret; + } + + /* pin releases bo's they are too messy to evict */ + ret = qxl_bo_reserve(qdev->current_release_bo[cur_idx], false); + qxl_bo_pin(qdev->current_release_bo[cur_idx], QXL_GEM_DOMAIN_VRAM, NULL); + qxl_bo_unreserve(qdev->current_release_bo[cur_idx]); + } + + bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]); + + (*release)->release_offset = qdev->current_release_bo_offset[cur_idx] * release_size_per_bo[cur_idx]; + qdev->current_release_bo_offset[cur_idx]++; + + if (rbo) + *rbo = bo; + + qxl_release_add_res(qdev, *release, bo); + + ret = qxl_release_reserve(qdev, *release, false); + mutex_unlock(&qdev->release_mutex); + if (ret) + goto out_unref; + + info = qxl_release_map(qdev, *release); + info->id = idr_ret; + qxl_release_unmap(qdev, *release, info); + +out_unref: + qxl_bo_unref(&bo); + return ret; +} + +int qxl_fence_releaseable(struct qxl_device *qdev, + struct qxl_release *release) +{ + int i, ret; + for (i = 0; i < release->bo_count; i++) { + if (!release->bos[i]->tbo.sync_obj) + release->bos[i]->tbo.sync_obj = &release->bos[i]->fence; + ret = qxl_fence_add_release(&release->bos[i]->fence, release->id); + if (ret) + return ret; + } + return 0; +} + +struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev, + uint64_t id) +{ + struct qxl_release *release; + + spin_lock(&qdev->release_idr_lock); + release = idr_find(&qdev->release_idr, id); + spin_unlock(&qdev->release_idr_lock); + if (!release) { + DRM_ERROR("failed to find id in release_idr\n"); + return NULL; + } + if (release->bo_count < 1) { + DRM_ERROR("read a released resource with 0 bos\n"); + return NULL; + } + return release; +} + +union qxl_release_info *qxl_release_map(struct qxl_device *qdev, + struct qxl_release *release) +{ + void *ptr; + union qxl_release_info *info; + struct qxl_bo *bo = release->bos[0]; + + ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_SIZE); + info = ptr + (release->release_offset & ~PAGE_SIZE); + return info; +} + +void qxl_release_unmap(struct qxl_device *qdev, + struct qxl_release *release, + union qxl_release_info *info) +{ + struct qxl_bo *bo = release->bos[0]; + void *ptr; + + ptr = ((void *)info) - (release->release_offset & ~PAGE_SIZE); + qxl_bo_kunmap_atomic_page(qdev, bo, ptr); +} diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c new file mode 100644 index 0000000..1a86242 --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -0,0 +1,577 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alon Levy + */ + +#include <ttm/ttm_bo_api.h> +#include <ttm/ttm_bo_driver.h> +#include <ttm/ttm_placement.h> +#include <ttm/ttm_page_alloc.h> +#include <ttm/ttm_module.h> +#include <drm/drmP.h> +#include <drm/drm.h> +#include <drm/qxl_drm.h> +#include "qxl_drv.h" +#include "qxl_object.h" + +#include <linux/delay.h> +static int qxl_ttm_debugfs_init(struct qxl_device *qdev); + +static struct qxl_device *qxl_get_qdev(struct ttm_bo_device *bdev) +{ + struct qxl_mman *mman; + struct qxl_device *qdev; + + mman = container_of(bdev, struct qxl_mman, bdev); + qdev = container_of(mman, struct qxl_device, mman); + return qdev; +} + +static int qxl_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void qxl_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int qxl_ttm_global_init(struct qxl_device *qdev) +{ + struct drm_global_reference *global_ref; + int r; + + qdev->mman.mem_global_referenced = false; + global_ref = &qdev->mman.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &qxl_ttm_mem_global_init; + global_ref->release = &qxl_ttm_mem_global_release; + + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + qdev->mman.bo_global_ref.mem_glob = + qdev->mman.mem_global_ref.object; + global_ref = &qdev->mman.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&qdev->mman.mem_global_ref); + return r; + } + + qdev->mman.mem_global_referenced = true; + return 0; +} + +static void qxl_ttm_global_fini(struct qxl_device *qdev) +{ + if (qdev->mman.mem_global_referenced) { + drm_global_item_unref(&qdev->mman.bo_global_ref.ref); + drm_global_item_unref(&qdev->mman.mem_global_ref); + qdev->mman.mem_global_referenced = false; + } +} + +static struct vm_operations_struct qxl_ttm_vm_ops; +static const struct vm_operations_struct *ttm_vm_ops; + +static int qxl_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo; + struct qxl_device *qdev; + int r; + + bo = (struct ttm_buffer_object *)vma->vm_private_data; + if (bo == NULL) + return VM_FAULT_NOPAGE; + qdev = qxl_get_qdev(bo->bdev); + r = ttm_vm_ops->fault(vma, vmf); + return r; +} + +int qxl_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct qxl_device *qdev; + int r; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { + pr_info("%s: vma->vm_pgoff (%ld) < DRM_FILE_PAGE_OFFSET\n", + __func__, vma->vm_pgoff); + return drm_mmap(filp, vma); + } + + file_priv = filp->private_data; + qdev = file_priv->minor->dev->dev_private; + if (qdev == NULL) { + DRM_ERROR( + "filp->private_data->minor->dev->dev_private == NULL\n"); + return -EINVAL; + } + QXL_INFO(qdev, "%s: filp->private_data = 0x%p, vma->vm_pgoff = %lx\n", + __func__, filp->private_data, vma->vm_pgoff); + + r = ttm_bo_mmap(filp, vma, &qdev->mman.bdev); + if (unlikely(r != 0)) + return r; + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + qxl_ttm_vm_ops = *ttm_vm_ops; + qxl_ttm_vm_ops.fault = &qxl_ttm_fault; + } + vma->vm_ops = &qxl_ttm_vm_ops; + return 0; +} + +static int qxl_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) +{ + return 0; +} + +static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + struct qxl_device *qdev; + + qdev = qxl_get_qdev(bdev); + + switch (type) { + case TTM_PL_SYSTEM: + /* System memory */ + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + case TTM_PL_PRIV0: + /* "On-card" video ram */ + man->func = &ttm_bo_manager_func; + man->gpu_offset = 0; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void qxl_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + struct qxl_bo *qbo; + static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + if (!qxl_ttm_bo_is_qxl_bo(bo)) { + placement->fpfn = 0; + placement->lpfn = 0; + placement->placement = &placements; + placement->busy_placement = &placements; + placement->num_placement = 1; + placement->num_busy_placement = 1; + return; + } + qbo = container_of(bo, struct qxl_bo, tbo); + qxl_ttm_placement_from_domain(qbo, QXL_GEM_DOMAIN_CPU); + *placement = qbo->placement; +} + +static int qxl_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + return 0; +} + +static int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct qxl_device *qdev = qxl_get_qdev(bdev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* system memory */ + return 0; + case TTM_PL_VRAM: + mem->bus.is_iomem = true; + mem->bus.base = qdev->vram_base; + mem->bus.offset = mem->start << PAGE_SHIFT; + break; + case TTM_PL_PRIV0: + mem->bus.is_iomem = true; + mem->bus.base = qdev->surfaceram_base; + mem->bus.offset = mem->start << PAGE_SHIFT; + break; + default: + return -EINVAL; + } + return 0; +} + +static void qxl_ttm_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ +} + +/* + * TTM backend functions. + */ +struct qxl_ttm_tt { + struct ttm_dma_tt ttm; + struct qxl_device *qdev; + u64 offset; +}; + +static int qxl_ttm_backend_bind(struct ttm_tt *ttm, + struct ttm_mem_reg *bo_mem) +{ + struct qxl_ttm_tt *gtt = (void *)ttm; + + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); + if (!ttm->num_pages) { + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", + ttm->num_pages, bo_mem, ttm); + } + /* Not implemented */ + return -1; +} + +static int qxl_ttm_backend_unbind(struct ttm_tt *ttm) +{ + /* Not implemented */ + return -1; +} + +static void qxl_ttm_backend_destroy(struct ttm_tt *ttm) +{ + struct qxl_ttm_tt *gtt = (void *)ttm; + + ttm_dma_tt_fini(>t->ttm); + kfree(gtt); +} + +static struct ttm_backend_func qxl_backend_func = { + .bind = &qxl_ttm_backend_bind, + .unbind = &qxl_ttm_backend_unbind, + .destroy = &qxl_ttm_backend_destroy, +}; + +static int qxl_ttm_tt_populate(struct ttm_tt *ttm) +{ + int r; + + if (ttm->state != tt_unpopulated) + return 0; + + r = ttm_pool_populate(ttm); + if (r) + return r; + + return 0; +} + +static void qxl_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +static struct ttm_tt *qxl_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, uint32_t page_flags, + struct page *dummy_read_page) +{ + struct qxl_device *qdev; + struct qxl_ttm_tt *gtt; + + qdev = qxl_get_qdev(bdev); + gtt = kzalloc(sizeof(struct qxl_ttm_tt), GFP_KERNEL); + if (gtt == NULL) + return NULL; + gtt->ttm.ttm.func = &qxl_backend_func; + gtt->qdev = qdev; + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags, + dummy_read_page)) { + kfree(gtt); + return NULL; + } + return >t->ttm.ttm; +} + +static void qxl_move_null(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + + BUG_ON(old_mem->mm_node != NULL); + *old_mem = *new_mem; + new_mem->mm_node = NULL; +} + +static int qxl_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { + qxl_move_null(bo, new_mem); + return 0; + } + return ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); +} + + +static int qxl_sync_obj_wait(void *sync_obj, + bool lazy, bool interruptible) +{ + struct qxl_fence *qfence = (struct qxl_fence *)sync_obj; + int count = 0, sc = 0; + struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence); + + if (qfence->num_active_releases == 0) + return 0; + +retry: + if (sc == 0) { + if (bo->type == QXL_GEM_DOMAIN_SURFACE) + qxl_update_surface(qfence->qdev, bo); + } else if (sc >= 1) { + qxl_io_notify_oom(qfence->qdev); + } + + sc++; + + for (count = 0; count < 10; count++) { + bool ret; + ret = qxl_queue_garbage_collect(qfence->qdev, true); + if (ret == false) + break; + + if (qfence->num_active_releases == 0) + return 0; + } + + if (qfence->num_active_releases) { + bool have_drawable_releases = false; + void **slot; + struct radix_tree_iter iter; + int release_id; + + radix_tree_for_each_slot(slot, &qfence->tree, &iter, 0) { + struct qxl_release *release; + + release_id = iter.index; + release = qxl_release_from_id_locked(qfence->qdev, release_id); + if (release == NULL) + continue; + + if (release->type == QXL_RELEASE_DRAWABLE) + have_drawable_releases = true; + } + + qxl_queue_garbage_collect(qfence->qdev, true); + + if (have_drawable_releases || sc < 4) { + if (sc > 2) + /* back off */ + usleep_range(500, 1000); + if (have_drawable_releases && sc > 300) { + WARN(1, "sync obj %d still has outstanding releases %d %d %d %ld %d\n", sc, bo->surface_id, bo->is_primary, bo->pin_count, (unsigned long)bo->gem_base.size, qfence->num_active_releases); + return -EBUSY; + } + goto retry; + } + } + return 0; +} + +static int qxl_sync_obj_flush(void *sync_obj) +{ + return 0; +} + +static void qxl_sync_obj_unref(void **sync_obj) +{ +} + +static void *qxl_sync_obj_ref(void *sync_obj) +{ + return sync_obj; +} + +static bool qxl_sync_obj_signaled(void *sync_obj) +{ + struct qxl_fence *qfence = (struct qxl_fence *)sync_obj; + return (qfence->num_active_releases == 0); +} + +static void qxl_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem) +{ + struct qxl_bo *qbo; + struct qxl_device *qdev; + + if (!qxl_ttm_bo_is_qxl_bo(bo)) + return; + qbo = container_of(bo, struct qxl_bo, tbo); + qdev = qbo->gem_base.dev->dev_private; + + if (bo->mem.mem_type == TTM_PL_PRIV0 && qbo->surface_id) + qxl_surface_evict(qdev, qbo, new_mem ? true : false); +} + +static struct ttm_bo_driver qxl_bo_driver = { + .ttm_tt_create = &qxl_ttm_tt_create, + .ttm_tt_populate = &qxl_ttm_tt_populate, + .ttm_tt_unpopulate = &qxl_ttm_tt_unpopulate, + .invalidate_caches = &qxl_invalidate_caches, + .init_mem_type = &qxl_init_mem_type, + .evict_flags = &qxl_evict_flags, + .move = &qxl_bo_move, + .verify_access = &qxl_verify_access, + .io_mem_reserve = &qxl_ttm_io_mem_reserve, + .io_mem_free = &qxl_ttm_io_mem_free, + .sync_obj_signaled = &qxl_sync_obj_signaled, + .sync_obj_wait = &qxl_sync_obj_wait, + .sync_obj_flush = &qxl_sync_obj_flush, + .sync_obj_unref = &qxl_sync_obj_unref, + .sync_obj_ref = &qxl_sync_obj_ref, + .move_notify = &qxl_bo_move_notify, +}; + + + +int qxl_ttm_init(struct qxl_device *qdev) +{ + int r; + int num_io_pages; /* != rom->num_io_pages, we include surface0 */ + + r = qxl_ttm_global_init(qdev); + if (r) + return r; + /* No others user of address space so set it to 0 */ + r = ttm_bo_device_init(&qdev->mman.bdev, + qdev->mman.bo_global_ref.ref.object, + &qxl_bo_driver, DRM_FILE_PAGE_OFFSET, 0); + if (r) { + DRM_ERROR("failed initializing buffer object driver(%d).\n", r); + return r; + } + /* NOTE: this includes the framebuffer (aka surface 0) */ + num_io_pages = qdev->rom->ram_header_offset / PAGE_SIZE; + r = ttm_bo_init_mm(&qdev->mman.bdev, TTM_PL_VRAM, + num_io_pages); + if (r) { + DRM_ERROR("Failed initializing VRAM heap.\n"); + return r; + } + r = ttm_bo_init_mm(&qdev->mman.bdev, TTM_PL_PRIV0, + qdev->surfaceram_size / PAGE_SIZE); + if (r) { + DRM_ERROR("Failed initializing Surfaces heap.\n"); + return r; + } + DRM_INFO("qxl: %uM of VRAM memory size\n", + (unsigned)qdev->vram_size / (1024 * 1024)); + DRM_INFO("qxl: %luM of IO pages memory ready (VRAM domain)\n", + ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024)); + if (unlikely(qdev->mman.bdev.dev_mapping == NULL)) + qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping; + r = qxl_ttm_debugfs_init(qdev); + if (r) { + DRM_ERROR("Failed to init debugfs\n"); + return r; + } + return 0; +} + +void qxl_ttm_fini(struct qxl_device *qdev) +{ + ttm_bo_clean_mm(&qdev->mman.bdev, TTM_PL_VRAM); + ttm_bo_clean_mm(&qdev->mman.bdev, TTM_PL_PRIV0); + ttm_bo_device_release(&qdev->mman.bdev); + qxl_ttm_global_fini(qdev); + DRM_INFO("qxl: ttm finalized\n"); +} + + +#define QXL_DEBUGFS_MEM_TYPES 2 + +#if defined(CONFIG_DEBUG_FS) +static int qxl_mm_dump_table(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_mm *mm = (struct drm_mm *)node->info_ent->data; + struct drm_device *dev = node->minor->dev; + struct qxl_device *rdev = dev->dev_private; + int ret; + struct ttm_bo_global *glob = rdev->mman.bdev.glob; + + spin_lock(&glob->lru_lock); + ret = drm_mm_dump_table(m, mm); + spin_unlock(&glob->lru_lock); + return ret; +} +#endif + +static int qxl_ttm_debugfs_init(struct qxl_device *qdev) +{ + static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES]; + static char qxl_mem_types_names[QXL_DEBUGFS_MEM_TYPES][32]; + unsigned i; + + for (i = 0; i < QXL_DEBUGFS_MEM_TYPES; i++) { + if (i == 0) + sprintf(qxl_mem_types_names[i], "qxl_mem_mm"); + else + sprintf(qxl_mem_types_names[i], "qxl_surf_mm"); + qxl_mem_types_list[i].name = qxl_mem_types_names[i]; + qxl_mem_types_list[i].show = &qxl_mm_dump_table; + qxl_mem_types_list[i].driver_features = 0; + if (i == 0) + qxl_mem_types_list[i].data = qdev->mman.bdev.man[TTM_PL_VRAM].priv; + else + qxl_mem_types_list[i].data = qdev->mman.bdev.man[TTM_PL_PRIV0].priv; + + } + return qxl_debugfs_add_files(qdev, qxl_mem_types_list, i); +} diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index bf17252..86c5e36 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -76,7 +76,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ - si_blit_shaders.o radeon_prime.o + si_blit_shaders.o radeon_prime.o radeon_uvd.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 21a892c..6d6fdb3 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -557,6 +557,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, /* use frac fb div on APUs */ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; + /* use frac fb div on RS780/RS880 */ + if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) + radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; if (ASIC_IS_DCE32(rdev) && mode->clock > 165000) radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; } else { diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 3c38ea4..124c193 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -84,6 +84,217 @@ void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw, } } +static int sumo_set_uvd_clock(struct radeon_device *rdev, u32 clock, + u32 cntl_reg, u32 status_reg) +{ + int r, i; + struct atom_clock_dividers dividers; + + r = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + clock, false, ÷rs); + if (r) + return r; + + WREG32_P(cntl_reg, dividers.post_div, ~(DCLK_DIR_CNTL_EN|DCLK_DIVIDER_MASK)); + + for (i = 0; i < 100; i++) { + if (RREG32(status_reg) & DCLK_STATUS) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; + + return 0; +} + +int sumo_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) +{ + int r = 0; + u32 cg_scratch = RREG32(CG_SCRATCH1); + + r = sumo_set_uvd_clock(rdev, vclk, CG_VCLK_CNTL, CG_VCLK_STATUS); + if (r) + goto done; + cg_scratch &= 0xffff0000; + cg_scratch |= vclk / 100; /* Mhz */ + + r = sumo_set_uvd_clock(rdev, dclk, CG_DCLK_CNTL, CG_DCLK_STATUS); + if (r) + goto done; + cg_scratch &= 0x0000ffff; + cg_scratch |= (dclk / 100) << 16; /* Mhz */ + +done: + WREG32(CG_SCRATCH1, cg_scratch); + + return r; +} + +static int evergreen_uvd_calc_post_div(unsigned target_freq, + unsigned vco_freq, + unsigned *div) +{ + /* target larger than vco frequency ? */ + if (vco_freq < target_freq) + return -1; /* forget it */ + + /* Fclk = Fvco / PDIV */ + *div = vco_freq / target_freq; + + /* we alway need a frequency less than or equal the target */ + if ((vco_freq / *div) > target_freq) + *div += 1; + + /* dividers above 5 must be even */ + if (*div > 5 && *div % 2) + *div += 1; + + /* out of range ? */ + if (*div >= 128) + return -1; /* forget it */ + + return vco_freq / *div; +} + +static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev) +{ + unsigned i; + + /* assert UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); + + /* wait for CTLACK and CTLACK2 to get asserted */ + for (i = 0; i < 100; ++i) { + uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; + if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; + + /* deassert UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); + + return 0; +} + +int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) +{ + /* start off with something large */ + int optimal_diff_score = 0x7FFFFFF; + unsigned optimal_fb_div = 0, optimal_vclk_div = 0; + unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; + unsigned vco_freq; + int r; + + /* loop through vco from low to high */ + for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { + unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; + int calc_clk, diff_score, diff_vclk, diff_dclk; + unsigned vclk_div, dclk_div; + + /* fb div out of range ? */ + if (fb_div > 0x03FFFFFF) + break; /* it can oly get worse */ + + /* calc vclk with current vco freq. */ + calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, &vclk_div); + if (calc_clk == -1) + break; /* vco is too big, it has to stop. */ + diff_vclk = vclk - calc_clk; + + /* calc dclk with current vco freq. */ + calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, &dclk_div); + if (calc_clk == -1) + break; /* vco is too big, it has to stop. */ + diff_dclk = dclk - calc_clk; + + /* determine if this vco setting is better than current optimal settings */ + diff_score = abs(diff_vclk) + abs(diff_dclk); + if (diff_score < optimal_diff_score) { + optimal_fb_div = fb_div; + optimal_vclk_div = vclk_div; + optimal_dclk_div = dclk_div; + optimal_vco_freq = vco_freq; + optimal_diff_score = diff_score; + if (optimal_diff_score == 0) + break; /* it can't get better than this */ + } + } + + /* set VCO_MODE to 1 */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); + + /* toggle UPLL_SLEEP to 1 then back to 0 */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK); + + /* deassert UPLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + mdelay(1); + + /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + /* put PLL in bypass mode */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); + + r = evergreen_uvd_send_upll_ctlreq(rdev); + if (r) + return r; + + /* assert UPLL_RESET again */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK); + + /* disable spread spectrum. */ + WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); + + /* set feedback divider */ + WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK); + + /* set ref divider to 0 */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); + + if (optimal_vco_freq < 187500) + WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); + else + WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); + + /* set PDIV_A and PDIV_B */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div), + ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); + + /* give the PLL some time to settle */ + mdelay(15); + + /* deassert PLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + mdelay(15); + + /* switch from bypass mode to normal mode */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); + + r = evergreen_uvd_send_upll_ctlreq(rdev); + if (r) + return r; + + /* switch VCLK and DCLK selection */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + mdelay(100); + + return 0; +} + void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev) { u16 ctl, v; @@ -608,6 +819,16 @@ void evergreen_hpd_init(struct radeon_device *rdev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* don't try to enable hpd on eDP or LVDS avoid breaking the + * aux dp channel on imac and help (but not completely fix) + * https://bugzilla.redhat.com/show_bug.cgi?id=726143 + * also avoid interrupt storms during dpms. + */ + continue; + } switch (radeon_connector->hpd.hpd) { case RADEON_HPD_1: WREG32(DC_HPD1_CONTROL, tmp); @@ -2050,6 +2271,14 @@ static void evergreen_gpu_init(struct radeon_device *rdev) } /* enabled rb are just the one not disabled :) */ disabled_rb_mask = tmp; + tmp = 0; + for (i = 0; i < rdev->config.evergreen.max_backends; i++) + tmp |= (1 << i); + /* if all the backends are disabled, fix it up here */ + if ((disabled_rb_mask & tmp) == tmp) { + for (i = 0; i < rdev->config.evergreen.max_backends; i++) + disabled_rb_mask &= ~(1 << i); + } WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); @@ -2058,6 +2287,9 @@ static void evergreen_gpu_init(struct radeon_device *rdev) WREG32(DMIF_ADDR_CONFIG, gb_addr_config); WREG32(HDP_ADDR_CONFIG, gb_addr_config); WREG32(DMA_TILING_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DB_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DBW_ADDR_CONFIG, gb_addr_config); if ((rdev->config.evergreen.max_backends == 1) && (rdev->flags & RADEON_IS_IGP)) { @@ -2438,6 +2670,12 @@ static u32 evergreen_gpu_check_soft_reset(struct radeon_device *rdev) if (tmp & L2_BUSY) reset_mask |= RADEON_RESET_VMC; + /* Skip MC reset as it's mostly likely not hung, just busy */ + if (reset_mask & RADEON_RESET_MC) { + DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask); + reset_mask &= ~RADEON_RESET_MC; + } + return reset_mask; } @@ -3354,6 +3592,9 @@ restart_ih: DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data); break; } + case 124: /* UVD */ + DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data); + radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX); break; case 146: case 147: @@ -3565,7 +3806,7 @@ int evergreen_copy_dma(struct radeon_device *rdev, static int evergreen_startup(struct radeon_device *rdev) { - struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + struct radeon_ring *ring; int r; /* enable pcie gen2 link */ @@ -3632,6 +3873,17 @@ static int evergreen_startup(struct radeon_device *rdev) return r; } + r = rv770_uvd_resume(rdev); + if (!r) { + r = radeon_fence_driver_start_ring(rdev, + R600_RING_TYPE_UVD_INDEX); + if (r) + dev_err(rdev->dev, "UVD fences init error (%d).\n", r); + } + + if (r) + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0; + /* Enable IRQ */ r = r600_irq_init(rdev); if (r) { @@ -3641,6 +3893,7 @@ static int evergreen_startup(struct radeon_device *rdev) } evergreen_irq_set(rdev); + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, R600_CP_RB_RPTR, R600_CP_RB_WPTR, 0, 0xfffff, RADEON_CP_PACKET2); @@ -3664,6 +3917,19 @@ static int evergreen_startup(struct radeon_device *rdev) if (r) return r; + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + if (ring->ring_size) { + r = radeon_ring_init(rdev, ring, ring->ring_size, + R600_WB_UVD_RPTR_OFFSET, + UVD_RBC_RB_RPTR, UVD_RBC_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (!r) + r = r600_uvd_init(rdev); + + if (r) + DRM_ERROR("radeon: error initializing UVD (%d).\n", r); + } + r = radeon_ib_pool_init(rdev); if (r) { dev_err(rdev->dev, "IB initialization failed (%d).\n", r); @@ -3710,8 +3976,10 @@ int evergreen_resume(struct radeon_device *rdev) int evergreen_suspend(struct radeon_device *rdev) { r600_audio_fini(rdev); + radeon_uvd_suspend(rdev); r700_cp_stop(rdev); r600_dma_stop(rdev); + r600_uvd_rbc_stop(rdev); evergreen_irq_suspend(rdev); radeon_wb_disable(rdev); evergreen_pcie_gart_disable(rdev); @@ -3791,6 +4059,13 @@ int evergreen_init(struct radeon_device *rdev) rdev->ring[R600_RING_TYPE_DMA_INDEX].ring_obj = NULL; r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX], 64 * 1024); + r = radeon_uvd_init(rdev); + if (!r) { + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX], + 4096); + } + rdev->ih.ring_obj = NULL; r600_ih_ring_init(rdev, 64 * 1024); @@ -3837,6 +4112,7 @@ void evergreen_fini(struct radeon_device *rdev) radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); evergreen_pcie_gart_fini(rdev); + radeon_uvd_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); @@ -3872,7 +4148,7 @@ void evergreen_pcie_gen2_enable(struct radeon_device *rdev) if (!(mask & DRM_PCIE_SPEED_50)) return; - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); if (speed_cntl & LC_CURRENT_DATA_RATE) { DRM_INFO("PCIE gen 2 link speeds already enabled\n"); return; @@ -3883,33 +4159,33 @@ void evergreen_pcie_gen2_enable(struct radeon_device *rdev) if ((speed_cntl & LC_OTHER_SIDE_EVER_SENT_GEN2) || (speed_cntl & LC_OTHER_SIDE_SUPPORTS_GEN2)) { - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); link_width_cntl &= ~LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl &= ~LC_TARGET_LINK_SPEED_OVERRIDE_EN; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl |= LC_CLR_FAILED_SPD_CHANGE_CNT; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl |= LC_GEN2_EN_STRAP; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); } else { - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ if (1) link_width_cntl |= LC_UPCONFIGURE_DIS; else link_width_cntl &= ~LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } } diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c index d8f5d5f..eb8ac31 100644 --- a/drivers/gpu/drm/radeon/evergreen_cs.c +++ b/drivers/gpu/drm/radeon/evergreen_cs.c @@ -834,7 +834,7 @@ static int evergreen_cs_track_validate_texture(struct radeon_cs_parser *p, __func__, __LINE__, toffset, surf.base_align); return -EINVAL; } - if (moffset & (surf.base_align - 1)) { + if (surf.nsamples <= 1 && moffset & (surf.base_align - 1)) { dev_warn(p->dev, "%s:%d mipmap bo base %ld not aligned with %ld\n", __func__, __LINE__, moffset, surf.base_align); return -EINVAL; @@ -2711,7 +2711,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) switch (sub_cmd) { /* tiled */ case 8: - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); @@ -2719,15 +2719,15 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; /* linear */ case 0: - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; p->idx += count + 3; break; default: - DRM_ERROR("bad DMA_PACKET_WRITE [%6d] 0x%08x sub cmd is not 0 or 8\n", idx, ib[idx+0]); + DRM_ERROR("bad DMA_PACKET_WRITE [%6d] 0x%08x sub cmd is not 0 or 8\n", idx, header); return -EINVAL; } if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { @@ -2751,10 +2751,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) /* Copy L2L, DW aligned */ case 0x00: /* L2L, dw */ - src_offset = ib[idx+2]; - src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { dev_warn(p->dev, "DMA L2L, dw src buffer too small (%llu %lu)\n", src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); @@ -2774,24 +2774,24 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) /* Copy L2T/T2L */ case 0x08: /* detile bit */ - if (ib[idx + 2] & (1 << 31)) { + if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) { /* tiled src, linear dst */ - src_offset = ib[idx+1]; + src_offset = radeon_get_ib_value(p, idx+1); src_offset <<= 8; ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); dst_offset = radeon_get_ib_value(p, idx + 7); - dst_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; } else { /* linear src, tiled dst */ - src_offset = ib[idx+7]; - src_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+7); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); } @@ -2810,10 +2810,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) /* Copy L2L, byte aligned */ case 0x40: /* L2L, byte */ - src_offset = ib[idx+2]; - src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; if ((src_offset + count) > radeon_bo_size(src_reloc->robj)) { dev_warn(p->dev, "DMA L2L, byte src buffer too small (%llu %lu)\n", src_offset + count, radeon_bo_size(src_reloc->robj)); @@ -2852,12 +2852,12 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) DRM_ERROR("bad L2L, dw, broadcast DMA_PACKET_COPY\n"); return -EINVAL; } - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; - dst2_offset = ib[idx+2]; - dst2_offset |= ((u64)(ib[idx+5] & 0xff)) << 32; - src_offset = ib[idx+3]; - src_offset |= ((u64)(ib[idx+6] & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst2_offset = radeon_get_ib_value(p, idx+2); + dst2_offset |= ((u64)(radeon_get_ib_value(p, idx+5) & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+3); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32; if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { dev_warn(p->dev, "DMA L2L, dw, broadcast src buffer too small (%llu %lu)\n", src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); @@ -2883,7 +2883,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; /* Copy L2T Frame to Field */ case 0x48: - if (ib[idx + 2] & (1 << 31)) { + if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) { DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); return -EINVAL; } @@ -2892,12 +2892,12 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); return -EINVAL; } - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; - dst2_offset = ib[idx+2]; + dst2_offset = radeon_get_ib_value(p, idx+2); dst2_offset <<= 8; - src_offset = ib[idx+8]; - src_offset |= ((u64)(ib[idx+9] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+8); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+9) & 0xff)) << 32; if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { dev_warn(p->dev, "DMA L2T, frame to fields src buffer too small (%llu %lu)\n", src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); @@ -2927,7 +2927,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } /* detile bit */ - if (ib[idx + 2 ] & (1 << 31)) { + if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) { /* tiled src, linear dst */ ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); @@ -2945,7 +2945,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) /* Copy L2T broadcast */ case 0x4b: /* L2T, broadcast */ - if (ib[idx + 2] & (1 << 31)) { + if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) { DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); return -EINVAL; } @@ -2954,12 +2954,12 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); return -EINVAL; } - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; - dst2_offset = ib[idx+2]; + dst2_offset = radeon_get_ib_value(p, idx+2); dst2_offset <<= 8; - src_offset = ib[idx+8]; - src_offset |= ((u64)(ib[idx+9] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+8); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+9) & 0xff)) << 32; if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%llu %lu)\n", src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); @@ -2985,24 +2985,24 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) case 0x4c: /* L2T, T2L */ /* detile bit */ - if (ib[idx + 2] & (1 << 31)) { + if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) { /* tiled src, linear dst */ - src_offset = ib[idx+1]; + src_offset = radeon_get_ib_value(p, idx+1); src_offset <<= 8; ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); - dst_offset = ib[idx+7]; - dst_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+7); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; } else { /* linear src, tiled dst */ - src_offset = ib[idx+7]; - src_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+7); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); } @@ -3032,7 +3032,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) /* Copy L2T broadcast (tile units) */ case 0x4f: /* L2T, broadcast */ - if (ib[idx + 2] & (1 << 31)) { + if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) { DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); return -EINVAL; } @@ -3041,12 +3041,12 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); return -EINVAL; } - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; - dst2_offset = ib[idx+2]; + dst2_offset = radeon_get_ib_value(p, idx+2); dst2_offset <<= 8; - src_offset = ib[idx+8]; - src_offset |= ((u64)(ib[idx+9] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+8); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+9) & 0xff)) << 32; if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%llu %lu)\n", src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); @@ -3069,7 +3069,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) p->idx += 10; break; default: - DRM_ERROR("bad DMA_PACKET_COPY [%6d] 0x%08x invalid sub cmd\n", idx, ib[idx+0]); + DRM_ERROR("bad DMA_PACKET_COPY [%6d] 0x%08x invalid sub cmd\n", idx, header); return -EINVAL; } break; @@ -3079,8 +3079,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) DRM_ERROR("bad DMA_PACKET_CONSTANT_FILL\n"); return -EINVAL; } - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+3] & 0x00ff0000)) << 16; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0x00ff0000)) << 16; if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { dev_warn(p->dev, "DMA constant fill buffer too small (%llu %lu)\n", dst_offset, radeon_bo_size(dst_reloc->robj)); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 982d25a..eabf92a 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -53,6 +53,43 @@ #define RCU_IND_INDEX 0x100 #define RCU_IND_DATA 0x104 +/* discrete uvd clocks */ +#define CG_UPLL_FUNC_CNTL 0x718 +# define UPLL_RESET_MASK 0x00000001 +# define UPLL_SLEEP_MASK 0x00000002 +# define UPLL_BYPASS_EN_MASK 0x00000004 +# define UPLL_CTLREQ_MASK 0x00000008 +# define UPLL_REF_DIV_MASK 0x001F0000 +# define UPLL_VCO_MODE_MASK 0x00000200 +# define UPLL_CTLACK_MASK 0x40000000 +# define UPLL_CTLACK2_MASK 0x80000000 +#define CG_UPLL_FUNC_CNTL_2 0x71c +# define UPLL_PDIV_A(x) ((x) << 0) +# define UPLL_PDIV_A_MASK 0x0000007F +# define UPLL_PDIV_B(x) ((x) << 8) +# define UPLL_PDIV_B_MASK 0x00007F00 +# define VCLK_SRC_SEL(x) ((x) << 20) +# define VCLK_SRC_SEL_MASK 0x01F00000 +# define DCLK_SRC_SEL(x) ((x) << 25) +# define DCLK_SRC_SEL_MASK 0x3E000000 +#define CG_UPLL_FUNC_CNTL_3 0x720 +# define UPLL_FB_DIV(x) ((x) << 0) +# define UPLL_FB_DIV_MASK 0x01FFFFFF +#define CG_UPLL_FUNC_CNTL_4 0x854 +# define UPLL_SPARE_ISPARE9 0x00020000 +#define CG_UPLL_SPREAD_SPECTRUM 0x79c +# define SSEN_MASK 0x00000001 + +/* fusion uvd clocks */ +#define CG_DCLK_CNTL 0x610 +# define DCLK_DIVIDER_MASK 0x7f +# define DCLK_DIR_CNTL_EN (1 << 8) +#define CG_DCLK_STATUS 0x614 +# define DCLK_STATUS (1 << 0) +#define CG_VCLK_CNTL 0x618 +#define CG_VCLK_STATUS 0x61c +#define CG_SCRATCH1 0x820 + #define GRBM_GFX_INDEX 0x802C #define INSTANCE_INDEX(x) ((x) << 0) #define SE_INDEX(x) ((x) << 16) @@ -992,6 +1029,16 @@ # define TARGET_LINK_SPEED_MASK (0xf << 0) # define SELECTABLE_DEEMPHASIS (1 << 6) + +/* + * UVD + */ +#define UVD_UDEC_ADDR_CONFIG 0xef4c +#define UVD_UDEC_DB_ADDR_CONFIG 0xef50 +#define UVD_UDEC_DBW_ADDR_CONFIG 0xef54 +#define UVD_RBC_RB_RPTR 0xf690 +#define UVD_RBC_RB_WPTR 0xf694 + /* * PM4 */ diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 7cead76..fd03f31 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -468,13 +468,19 @@ static void cayman_gpu_init(struct radeon_device *rdev) (rdev->pdev->device == 0x9907) || (rdev->pdev->device == 0x9908) || (rdev->pdev->device == 0x9909) || + (rdev->pdev->device == 0x990B) || + (rdev->pdev->device == 0x990C) || + (rdev->pdev->device == 0x990F) || (rdev->pdev->device == 0x9910) || - (rdev->pdev->device == 0x9917)) { + (rdev->pdev->device == 0x9917) || + (rdev->pdev->device == 0x9999)) { rdev->config.cayman.max_simds_per_se = 6; rdev->config.cayman.max_backends_per_se = 2; } else if ((rdev->pdev->device == 0x9903) || (rdev->pdev->device == 0x9904) || (rdev->pdev->device == 0x990A) || + (rdev->pdev->device == 0x990D) || + (rdev->pdev->device == 0x990E) || (rdev->pdev->device == 0x9913) || (rdev->pdev->device == 0x9918)) { rdev->config.cayman.max_simds_per_se = 4; @@ -483,6 +489,9 @@ static void cayman_gpu_init(struct radeon_device *rdev) (rdev->pdev->device == 0x9990) || (rdev->pdev->device == 0x9991) || (rdev->pdev->device == 0x9994) || + (rdev->pdev->device == 0x9995) || + (rdev->pdev->device == 0x9996) || + (rdev->pdev->device == 0x999A) || (rdev->pdev->device == 0x99A0)) { rdev->config.cayman.max_simds_per_se = 3; rdev->config.cayman.max_backends_per_se = 1; @@ -606,21 +615,45 @@ static void cayman_gpu_init(struct radeon_device *rdev) } /* enabled rb are just the one not disabled :) */ disabled_rb_mask = tmp; + tmp = 0; + for (i = 0; i < (rdev->config.cayman.max_backends_per_se * rdev->config.cayman.max_shader_engines); i++) + tmp |= (1 << i); + /* if all the backends are disabled, fix it up here */ + if ((disabled_rb_mask & tmp) == tmp) { + for (i = 0; i < (rdev->config.cayman.max_backends_per_se * rdev->config.cayman.max_shader_engines); i++) + disabled_rb_mask &= ~(1 << i); + } WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + if (ASIC_IS_DCE6(rdev)) + WREG32(DMIF_ADDR_CALC, gb_addr_config); WREG32(HDP_ADDR_CONFIG, gb_addr_config); WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config); WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config); - - tmp = gb_addr_config & NUM_PIPES_MASK; - tmp = r6xx_remap_render_backend(rdev, tmp, - rdev->config.cayman.max_backends_per_se * - rdev->config.cayman.max_shader_engines, - CAYMAN_MAX_BACKENDS, disabled_rb_mask); + WREG32(UVD_UDEC_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DB_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DBW_ADDR_CONFIG, gb_addr_config); + + if ((rdev->config.cayman.max_backends_per_se == 1) && + (rdev->flags & RADEON_IS_IGP)) { + if ((disabled_rb_mask & 3) == 1) { + /* RB0 disabled, RB1 enabled */ + tmp = 0x11111111; + } else { + /* RB1 disabled, RB0 enabled */ + tmp = 0x00000000; + } + } else { + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, + rdev->config.cayman.max_backends_per_se * + rdev->config.cayman.max_shader_engines, + CAYMAN_MAX_BACKENDS, disabled_rb_mask); + } WREG32(GB_BACKEND_MAP, tmp); cgts_tcc_disable = 0xffff0000; @@ -911,6 +944,23 @@ void cayman_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) radeon_ring_write(ring, 10); /* poll interval */ } +void cayman_uvd_semaphore_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + uint64_t addr = semaphore->gpu_addr; + + radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_LOW, 0)); + radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF); + + radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_HIGH, 0)); + radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF); + + radeon_ring_write(ring, PACKET0(UVD_SEMA_CMD, 0)); + radeon_ring_write(ring, 0x80 | (emit_wait ? 1 : 0)); +} + static void cayman_cp_enable(struct radeon_device *rdev, bool enable) { if (enable) @@ -1381,6 +1431,12 @@ static u32 cayman_gpu_check_soft_reset(struct radeon_device *rdev) if (tmp & L2_BUSY) reset_mask |= RADEON_RESET_VMC; + /* Skip MC reset as it's mostly likely not hung, just busy */ + if (reset_mask & RADEON_RESET_MC) { + DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask); + reset_mask &= ~RADEON_RESET_MC; + } + return reset_mask; } @@ -1656,6 +1712,16 @@ static int cayman_startup(struct radeon_device *rdev) return r; } + r = rv770_uvd_resume(rdev); + if (!r) { + r = radeon_fence_driver_start_ring(rdev, + R600_RING_TYPE_UVD_INDEX); + if (r) + dev_err(rdev->dev, "UVD fences init error (%d).\n", r); + } + if (r) + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0; + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX); if (r) { dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); @@ -1722,6 +1788,18 @@ static int cayman_startup(struct radeon_device *rdev) if (r) return r; + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + if (ring->ring_size) { + r = radeon_ring_init(rdev, ring, ring->ring_size, + R600_WB_UVD_RPTR_OFFSET, + UVD_RBC_RB_RPTR, UVD_RBC_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (!r) + r = r600_uvd_init(rdev); + if (r) + DRM_ERROR("radeon: failed initializing UVD (%d).\n", r); + } + r = radeon_ib_pool_init(rdev); if (r) { dev_err(rdev->dev, "IB initialization failed (%d).\n", r); @@ -1765,8 +1843,11 @@ int cayman_resume(struct radeon_device *rdev) int cayman_suspend(struct radeon_device *rdev) { r600_audio_fini(rdev); + radeon_vm_manager_fini(rdev); cayman_cp_enable(rdev, false); cayman_dma_stop(rdev); + r600_uvd_rbc_stop(rdev); + radeon_uvd_suspend(rdev); evergreen_irq_suspend(rdev); radeon_wb_disable(rdev); cayman_pcie_gart_disable(rdev); @@ -1841,6 +1922,13 @@ int cayman_init(struct radeon_device *rdev) ring->ring_obj = NULL; r600_ring_init(rdev, ring, 64 * 1024); + r = radeon_uvd_init(rdev); + if (!r) { + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 4096); + } + rdev->ih.ring_obj = NULL; r600_ih_ring_init(rdev, 64 * 1024); @@ -1892,6 +1980,7 @@ void cayman_fini(struct radeon_device *rdev) radeon_vm_manager_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); + radeon_uvd_fini(rdev); cayman_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); @@ -1990,28 +2079,57 @@ void cayman_vm_set_page(struct radeon_device *rdev, } } } else { - while (count) { - ndw = count * 2; - if (ndw > 0xFFFFE) - ndw = 0xFFFFE; + if ((flags & RADEON_VM_PAGE_SYSTEM) || + (count == 1)) { + while (count) { + ndw = count * 2; + if (ndw > 0xFFFFE) + ndw = 0xFFFFE; + + /* for non-physically contiguous pages (system) */ + ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, ndw); + ib->ptr[ib->length_dw++] = pe; + ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff; + for (; ndw > 0; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + ib->ptr[ib->length_dw++] = value; + ib->ptr[ib->length_dw++] = upper_32_bits(value); + } + } + while (ib->length_dw & 0x7) + ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0); + } else { + while (count) { + ndw = count * 2; + if (ndw > 0xFFFFE) + ndw = 0xFFFFE; - /* for non-physically contiguous pages (system) */ - ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, ndw); - ib->ptr[ib->length_dw++] = pe; - ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff; - for (; ndw > 0; ndw -= 2, --count, pe += 8) { - if (flags & RADEON_VM_PAGE_SYSTEM) { - value = radeon_vm_map_gart(rdev, addr); - value &= 0xFFFFFFFFFFFFF000ULL; - } else if (flags & RADEON_VM_PAGE_VALID) { + if (flags & RADEON_VM_PAGE_VALID) value = addr; - } else { + else value = 0; - } - addr += incr; - value |= r600_flags; - ib->ptr[ib->length_dw++] = value; + /* for physically contiguous pages (vram) */ + ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw); + ib->ptr[ib->length_dw++] = pe; /* dst addr */ + ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff; + ib->ptr[ib->length_dw++] = r600_flags; /* mask */ + ib->ptr[ib->length_dw++] = 0; + ib->ptr[ib->length_dw++] = value; /* value */ ib->ptr[ib->length_dw++] = upper_32_bits(value); + ib->ptr[ib->length_dw++] = incr; /* increment size */ + ib->ptr[ib->length_dw++] = 0; + pe += ndw * 4; + addr += (ndw / 2) * incr; + count -= ndw / 2; } } while (ib->length_dw & 0x7) diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 079dee2..e226faf 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -45,6 +45,10 @@ #define ARUBA_GB_ADDR_CONFIG_GOLDEN 0x12010001 #define DMIF_ADDR_CONFIG 0xBD4 + +/* DCE6 only */ +#define DMIF_ADDR_CALC 0xC00 + #define SRBM_GFX_CNTL 0x0E44 #define RINGID(x) (((x) & 0x3) << 0) #define VMID(x) (((x) & 0x7) << 0) @@ -486,6 +490,18 @@ # define CACHE_FLUSH_AND_INV_EVENT (0x16 << 0) /* + * UVD + */ +#define UVD_SEMA_ADDR_LOW 0xEF00 +#define UVD_SEMA_ADDR_HIGH 0xEF04 +#define UVD_SEMA_CMD 0xEF08 +#define UVD_UDEC_ADDR_CONFIG 0xEF4C +#define UVD_UDEC_DB_ADDR_CONFIG 0xEF50 +#define UVD_UDEC_DBW_ADDR_CONFIG 0xEF54 +#define UVD_RBC_RB_RPTR 0xF690 +#define UVD_RBC_RB_WPTR 0xF694 + +/* * PM4 */ #define PACKET0(reg, n) ((RADEON_PACKET_TYPE0 << 30) | \ @@ -668,6 +684,11 @@ (((vmid) & 0xF) << 20) | \ (((n) & 0xFFFFF) << 0)) +#define DMA_PTE_PDE_PACKET(n) ((2 << 28) | \ + (1 << 26) | \ + (1 << 21) | \ + (((n) & 0xFFFFF) << 0)) + /* async DMA Packet types */ #define DMA_PACKET_WRITE 0x2 #define DMA_PACKET_COPY 0x3 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 6d4b561..5fe9e74 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1145,7 +1145,7 @@ static void r600_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc } if (rdev->flags & RADEON_IS_AGP) { size_bf = mc->gtt_start; - size_af = 0xFFFFFFFF - mc->gtt_end; + size_af = mc->mc_mask - mc->gtt_end; if (size_bf > size_af) { if (mc->mc_vram_size > size_bf) { dev_warn(rdev->dev, "limiting VRAM\n"); @@ -1394,6 +1394,12 @@ static u32 r600_gpu_check_soft_reset(struct radeon_device *rdev) if (r600_is_display_hung(rdev)) reset_mask |= RADEON_RESET_DISPLAY; + /* Skip MC reset as it's mostly likely not hung, just busy */ + if (reset_mask & RADEON_RESET_MC) { + DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask); + reset_mask &= ~RADEON_RESET_MC; + } + return reset_mask; } @@ -2546,6 +2552,185 @@ void r600_dma_fini(struct radeon_device *rdev) } /* + * UVD + */ +int r600_uvd_rbc_start(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + uint64_t rptr_addr; + uint32_t rb_bufsz, tmp; + int r; + + rptr_addr = rdev->wb.gpu_addr + R600_WB_UVD_RPTR_OFFSET; + + if (upper_32_bits(rptr_addr) != upper_32_bits(ring->gpu_addr)) { + DRM_ERROR("UVD ring and rptr not in the same 4GB segment!\n"); + return -EINVAL; + } + + /* force RBC into idle state */ + WREG32(UVD_RBC_RB_CNTL, 0x11010101); + + /* Set the write pointer delay */ + WREG32(UVD_RBC_RB_WPTR_CNTL, 0); + + /* set the wb address */ + WREG32(UVD_RBC_RB_RPTR_ADDR, rptr_addr >> 2); + + /* programm the 4GB memory segment for rptr and ring buffer */ + WREG32(UVD_LMI_EXT40_ADDR, upper_32_bits(rptr_addr) | + (0x7 << 16) | (0x1 << 31)); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(UVD_RBC_RB_RPTR, 0x0); + + ring->wptr = ring->rptr = RREG32(UVD_RBC_RB_RPTR); + WREG32(UVD_RBC_RB_WPTR, ring->wptr); + + /* set the ring address */ + WREG32(UVD_RBC_RB_BASE, ring->gpu_addr); + + /* Set ring buffer size */ + rb_bufsz = drm_order(ring->ring_size); + rb_bufsz = (0x1 << 8) | rb_bufsz; + WREG32(UVD_RBC_RB_CNTL, rb_bufsz); + + ring->ready = true; + r = radeon_ring_test(rdev, R600_RING_TYPE_UVD_INDEX, ring); + if (r) { + ring->ready = false; + return r; + } + + r = radeon_ring_lock(rdev, ring, 10); + if (r) { + DRM_ERROR("radeon: ring failed to lock UVD ring (%d).\n", r); + return r; + } + + tmp = PACKET0(UVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL, 0); + radeon_ring_write(ring, tmp); + radeon_ring_write(ring, 0xFFFFF); + + tmp = PACKET0(UVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL, 0); + radeon_ring_write(ring, tmp); + radeon_ring_write(ring, 0xFFFFF); + + tmp = PACKET0(UVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL, 0); + radeon_ring_write(ring, tmp); + radeon_ring_write(ring, 0xFFFFF); + + /* Clear timeout status bits */ + radeon_ring_write(ring, PACKET0(UVD_SEMA_TIMEOUT_STATUS, 0)); + radeon_ring_write(ring, 0x8); + + radeon_ring_write(ring, PACKET0(UVD_SEMA_CNTL, 0)); + radeon_ring_write(ring, 1); + + radeon_ring_unlock_commit(rdev, ring); + + return 0; +} + +void r600_uvd_rbc_stop(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + + /* force RBC into idle state */ + WREG32(UVD_RBC_RB_CNTL, 0x11010101); + ring->ready = false; +} + +int r600_uvd_init(struct radeon_device *rdev) +{ + int i, j, r; + + /* disable clock gating */ + WREG32(UVD_CGC_GATE, 0); + + /* disable interupt */ + WREG32_P(UVD_MASTINT_EN, 0, ~(1 << 1)); + + /* put LMI, VCPU, RBC etc... into reset */ + WREG32(UVD_SOFT_RESET, LMI_SOFT_RESET | VCPU_SOFT_RESET | + LBSI_SOFT_RESET | RBC_SOFT_RESET | CSM_SOFT_RESET | + CXW_SOFT_RESET | TAP_SOFT_RESET | LMI_UMC_SOFT_RESET); + mdelay(5); + + /* take UVD block out of reset */ + WREG32_P(SRBM_SOFT_RESET, 0, ~SOFT_RESET_UVD); + mdelay(5); + + /* initialize UVD memory controller */ + WREG32(UVD_LMI_CTRL, 0x40 | (1 << 8) | (1 << 13) | + (1 << 21) | (1 << 9) | (1 << 20)); + + /* disable byte swapping */ + WREG32(UVD_LMI_SWAP_CNTL, 0); + WREG32(UVD_MP_SWAP_CNTL, 0); + + WREG32(UVD_MPC_SET_MUXA0, 0x40c2040); + WREG32(UVD_MPC_SET_MUXA1, 0x0); + WREG32(UVD_MPC_SET_MUXB0, 0x40c2040); + WREG32(UVD_MPC_SET_MUXB1, 0x0); + WREG32(UVD_MPC_SET_ALU, 0); + WREG32(UVD_MPC_SET_MUX, 0x88); + + /* Stall UMC */ + WREG32_P(UVD_LMI_CTRL2, 1 << 8, ~(1 << 8)); + WREG32_P(UVD_RB_ARB_CTRL, 1 << 3, ~(1 << 3)); + + /* take all subblocks out of reset, except VCPU */ + WREG32(UVD_SOFT_RESET, VCPU_SOFT_RESET); + mdelay(5); + + /* enable VCPU clock */ + WREG32(UVD_VCPU_CNTL, 1 << 9); + + /* enable UMC */ + WREG32_P(UVD_LMI_CTRL2, 0, ~(1 << 8)); + + /* boot up the VCPU */ + WREG32(UVD_SOFT_RESET, 0); + mdelay(10); + + WREG32_P(UVD_RB_ARB_CTRL, 0, ~(1 << 3)); + + for (i = 0; i < 10; ++i) { + uint32_t status; + for (j = 0; j < 100; ++j) { + status = RREG32(UVD_STATUS); + if (status & 2) + break; + mdelay(10); + } + r = 0; + if (status & 2) + break; + + DRM_ERROR("UVD not responding, trying to reset the VCPU!!!\n"); + WREG32_P(UVD_SOFT_RESET, VCPU_SOFT_RESET, ~VCPU_SOFT_RESET); + mdelay(10); + WREG32_P(UVD_SOFT_RESET, 0, ~VCPU_SOFT_RESET); + mdelay(10); + r = -1; + } + if (r) { + DRM_ERROR("UVD not responding, giving up!!!\n"); + return r; + } + /* enable interupt */ + WREG32_P(UVD_MASTINT_EN, 3<<1, ~(3 << 1)); + + r = r600_uvd_rbc_start(rdev); + if (r) + return r; + + DRM_INFO("UVD initialized successfully.\n"); + return 0; +} + +/* * GPU scratch registers helpers function. */ void r600_scratch_init(struct radeon_device *rdev) @@ -2654,6 +2839,40 @@ int r600_dma_ring_test(struct radeon_device *rdev, return r; } +int r600_uvd_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + uint32_t tmp = 0; + unsigned i; + int r; + + WREG32(UVD_CONTEXT_ID, 0xCAFEDEAD); + r = radeon_ring_lock(rdev, ring, 3); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring %d (%d).\n", + ring->idx, r); + return r; + } + radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0)); + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(UVD_CONTEXT_ID); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + + if (i < rdev->usec_timeout) { + DRM_INFO("ring test on %d succeeded in %d usecs\n", + ring->idx, i); + } else { + DRM_ERROR("radeon: ring %d test failed (0x%08X)\n", + ring->idx, tmp); + r = -EINVAL; + } + return r; +} + /* * CP fences/semaphores */ @@ -2705,6 +2924,30 @@ void r600_fence_ring_emit(struct radeon_device *rdev, } } +void r600_uvd_fence_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + uint32_t addr = rdev->fence_drv[fence->ring].gpu_addr; + + radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0)); + radeon_ring_write(ring, upper_32_bits(addr) & 0xff); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0)); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0)); + radeon_ring_write(ring, 2); + return; +} + void r600_semaphore_ring_emit(struct radeon_device *rdev, struct radeon_ring *ring, struct radeon_semaphore *semaphore, @@ -2774,6 +3017,23 @@ void r600_dma_semaphore_ring_emit(struct radeon_device *rdev, radeon_ring_write(ring, upper_32_bits(addr) & 0xff); } +void r600_uvd_semaphore_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + uint64_t addr = semaphore->gpu_addr; + + radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_LOW, 0)); + radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF); + + radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_HIGH, 0)); + radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF); + + radeon_ring_write(ring, PACKET0(UVD_SEMA_CMD, 0)); + radeon_ring_write(ring, emit_wait ? 1 : 0); +} + int r600_copy_blit(struct radeon_device *rdev, uint64_t src_offset, uint64_t dst_offset, @@ -3177,6 +3437,16 @@ void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) radeon_ring_write(ring, ib->length_dw); } +void r600_uvd_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + + radeon_ring_write(ring, PACKET0(UVD_RBC_IB_BASE, 0)); + radeon_ring_write(ring, ib->gpu_addr); + radeon_ring_write(ring, PACKET0(UVD_RBC_IB_SIZE, 0)); + radeon_ring_write(ring, ib->length_dw); +} + int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) { struct radeon_ib ib; @@ -3294,6 +3564,33 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) return r; } +int r600_uvd_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + struct radeon_fence *fence; + int r; + + r = radeon_uvd_get_create_msg(rdev, ring->idx, 1, NULL); + if (r) { + DRM_ERROR("radeon: failed to get create msg (%d).\n", r); + return r; + } + + r = radeon_uvd_get_destroy_msg(rdev, ring->idx, 1, &fence); + if (r) { + DRM_ERROR("radeon: failed to get destroy ib (%d).\n", r); + return r; + } + + r = radeon_fence_wait(fence, false); + if (r) { + DRM_ERROR("radeon: fence wait failed (%d).\n", r); + return r; + } + DRM_INFO("ib test on ring %d succeeded\n", ring->idx); + radeon_fence_unref(&fence); + return r; +} + /** * r600_dma_ring_ib_execute - Schedule an IB on the DMA engine * @@ -4226,7 +4523,7 @@ void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo) void r600_set_pcie_lanes(struct radeon_device *rdev, int lanes) { - u32 link_width_cntl, mask, target_reg; + u32 link_width_cntl, mask; if (rdev->flags & RADEON_IS_IGP) return; @@ -4238,7 +4535,7 @@ void r600_set_pcie_lanes(struct radeon_device *rdev, int lanes) if (ASIC_IS_X2(rdev)) return; - /* FIXME wait for idle */ + radeon_gui_idle(rdev); switch (lanes) { case 0: @@ -4257,53 +4554,24 @@ void r600_set_pcie_lanes(struct radeon_device *rdev, int lanes) mask = RADEON_PCIE_LC_LINK_WIDTH_X8; break; case 12: + /* not actually supported */ mask = RADEON_PCIE_LC_LINK_WIDTH_X12; break; case 16: - default: mask = RADEON_PCIE_LC_LINK_WIDTH_X16; break; - } - - link_width_cntl = RREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL); - - if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == - (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) - return; - - if (link_width_cntl & R600_PCIE_LC_UPCONFIGURE_DIS) + default: + DRM_ERROR("invalid pcie lane request: %d\n", lanes); return; + } - link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK | - RADEON_PCIE_LC_RECONFIG_NOW | - R600_PCIE_LC_RENEGOTIATE_EN | - R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE); - link_width_cntl |= mask; - - WREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); - - /* some northbridges can renegotiate the link rather than requiring - * a complete re-config. - * e.g., AMD 780/790 northbridges (pci ids: 0x5956, 0x5957, 0x5958, etc.) - */ - if (link_width_cntl & R600_PCIE_LC_RENEGOTIATION_SUPPORT) - link_width_cntl |= R600_PCIE_LC_RENEGOTIATE_EN | R600_PCIE_LC_UPCONFIGURE_SUPPORT; - else - link_width_cntl |= R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE; - - WREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL, (link_width_cntl | - RADEON_PCIE_LC_RECONFIG_NOW)); - - if (rdev->family >= CHIP_RV770) - target_reg = R700_TARGET_AND_CURRENT_PROFILE_INDEX; - else - target_reg = R600_TARGET_AND_CURRENT_PROFILE_INDEX; - - /* wait for lane set to complete */ - link_width_cntl = RREG32(target_reg); - while (link_width_cntl == 0xffffffff) - link_width_cntl = RREG32(target_reg); + link_width_cntl = RREG32_PCIE_PORT(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl &= ~RADEON_PCIE_LC_LINK_WIDTH_MASK; + link_width_cntl |= mask << RADEON_PCIE_LC_LINK_WIDTH_SHIFT; + link_width_cntl |= (RADEON_PCIE_LC_RECONFIG_NOW | + R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE); + WREG32_PCIE_PORT(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } int r600_get_pcie_lanes(struct radeon_device *rdev) @@ -4320,13 +4588,11 @@ int r600_get_pcie_lanes(struct radeon_device *rdev) if (ASIC_IS_X2(rdev)) return 0; - /* FIXME wait for idle */ + radeon_gui_idle(rdev); - link_width_cntl = RREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(RADEON_PCIE_LC_LINK_WIDTH_CNTL); switch ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) >> RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT) { - case RADEON_PCIE_LC_LINK_WIDTH_X0: - return 0; case RADEON_PCIE_LC_LINK_WIDTH_X1: return 1; case RADEON_PCIE_LC_LINK_WIDTH_X2: @@ -4335,6 +4601,10 @@ int r600_get_pcie_lanes(struct radeon_device *rdev) return 4; case RADEON_PCIE_LC_LINK_WIDTH_X8: return 8; + case RADEON_PCIE_LC_LINK_WIDTH_X12: + /* not actually supported */ + return 12; + case RADEON_PCIE_LC_LINK_WIDTH_X0: case RADEON_PCIE_LC_LINK_WIDTH_X16: default: return 16; @@ -4372,7 +4642,7 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev) if (!(mask & DRM_PCIE_SPEED_50)) return; - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); if (speed_cntl & LC_CURRENT_DATA_RATE) { DRM_INFO("PCIE gen 2 link speeds already enabled\n"); return; @@ -4385,23 +4655,23 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev) (rdev->family == CHIP_RV620) || (rdev->family == CHIP_RV635)) { /* advertise upconfig capability */ - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); link_width_cntl &= ~LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); if (link_width_cntl & LC_RENEGOTIATION_SUPPORT) { lanes = (link_width_cntl & LC_LINK_WIDTH_RD_MASK) >> LC_LINK_WIDTH_RD_SHIFT; link_width_cntl &= ~(LC_LINK_WIDTH_MASK | LC_RECONFIG_ARC_MISSING_ESCAPE); link_width_cntl |= lanes | LC_RECONFIG_NOW | LC_RENEGOTIATE_EN; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } else { link_width_cntl |= LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } } - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); if ((speed_cntl & LC_OTHER_SIDE_EVER_SENT_GEN2) && (speed_cntl & LC_OTHER_SIDE_SUPPORTS_GEN2)) { @@ -4422,7 +4692,7 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev) speed_cntl &= ~LC_VOLTAGE_TIMER_SEL_MASK; speed_cntl &= ~LC_FORCE_DIS_HW_SPEED_CHANGE; speed_cntl |= LC_FORCE_EN_HW_SPEED_CHANGE; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); tmp = RREG32(0x541c); WREG32(0x541c, tmp | 0x8); @@ -4436,27 +4706,27 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev) if ((rdev->family == CHIP_RV670) || (rdev->family == CHIP_RV620) || (rdev->family == CHIP_RV635)) { - training_cntl = RREG32_PCIE_P(PCIE_LC_TRAINING_CNTL); + training_cntl = RREG32_PCIE_PORT(PCIE_LC_TRAINING_CNTL); training_cntl &= ~LC_POINT_7_PLUS_EN; - WREG32_PCIE_P(PCIE_LC_TRAINING_CNTL, training_cntl); + WREG32_PCIE_PORT(PCIE_LC_TRAINING_CNTL, training_cntl); } else { - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl &= ~LC_TARGET_LINK_SPEED_OVERRIDE_EN; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); } - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl |= LC_GEN2_EN_STRAP; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); } else { - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ if (1) link_width_cntl |= LC_UPCONFIGURE_DIS; else link_width_cntl &= ~LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } } diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 931a702..01a3ec8 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -2491,14 +2491,14 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } if (tiled) { - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); p->idx += count + 5; } else { - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; @@ -2526,32 +2526,32 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) /* detile bit */ if (idx_value & (1 << 31)) { /* tiled src, linear dst */ - src_offset = ib[idx+1]; + src_offset = radeon_get_ib_value(p, idx+1); src_offset <<= 8; ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); - dst_offset = ib[idx+5]; - dst_offset |= ((u64)(ib[idx+6] & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+5); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32; ib[idx+5] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+6] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; } else { /* linear src, tiled dst */ - src_offset = ib[idx+5]; - src_offset |= ((u64)(ib[idx+6] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+5); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32; ib[idx+5] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; - dst_offset = ib[idx+1]; + dst_offset = radeon_get_ib_value(p, idx+1); dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); } p->idx += 7; } else { if (p->family >= CHIP_RV770) { - src_offset = ib[idx+2]; - src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); @@ -2559,10 +2559,10 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; p->idx += 5; } else { - src_offset = ib[idx+2]; - src_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+3] & 0xff0000)) << 16; + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff0000)) << 16; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); @@ -2592,8 +2592,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) DRM_ERROR("bad DMA_PACKET_WRITE\n"); return -EINVAL; } - dst_offset = ib[idx+1]; - dst_offset |= ((u64)(ib[idx+3] & 0x00ff0000)) << 16; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0x00ff0000)) << 16; if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { dev_warn(p->dev, "DMA constant fill buffer too small (%llu %lu)\n", dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index a42ba11..441bdb8 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -691,6 +691,7 @@ #define SRBM_SOFT_RESET 0xe60 # define SOFT_RESET_DMA (1 << 12) # define SOFT_RESET_RLC (1 << 13) +# define SOFT_RESET_UVD (1 << 18) # define RV770_SOFT_RESET_DMA (1 << 20) #define CP_INT_CNTL 0xc124 @@ -1143,6 +1144,66 @@ # define AFMT_AZ_AUDIO_ENABLE_CHG_ACK (1 << 30) /* + * UVD + */ +#define UVD_SEMA_ADDR_LOW 0xef00 +#define UVD_SEMA_ADDR_HIGH 0xef04 +#define UVD_SEMA_CMD 0xef08 + +#define UVD_GPCOM_VCPU_CMD 0xef0c +#define UVD_GPCOM_VCPU_DATA0 0xef10 +#define UVD_GPCOM_VCPU_DATA1 0xef14 +#define UVD_ENGINE_CNTL 0xef18 + +#define UVD_SEMA_CNTL 0xf400 +#define UVD_RB_ARB_CTRL 0xf480 + +#define UVD_LMI_EXT40_ADDR 0xf498 +#define UVD_CGC_GATE 0xf4a8 +#define UVD_LMI_CTRL2 0xf4f4 +#define UVD_MASTINT_EN 0xf500 +#define UVD_LMI_ADDR_EXT 0xf594 +#define UVD_LMI_CTRL 0xf598 +#define UVD_LMI_SWAP_CNTL 0xf5b4 +#define UVD_MP_SWAP_CNTL 0xf5bC +#define UVD_MPC_CNTL 0xf5dC +#define UVD_MPC_SET_MUXA0 0xf5e4 +#define UVD_MPC_SET_MUXA1 0xf5e8 +#define UVD_MPC_SET_MUXB0 0xf5eC +#define UVD_MPC_SET_MUXB1 0xf5f0 +#define UVD_MPC_SET_MUX 0xf5f4 +#define UVD_MPC_SET_ALU 0xf5f8 + +#define UVD_VCPU_CNTL 0xf660 +#define UVD_SOFT_RESET 0xf680 +#define RBC_SOFT_RESET (1<<0) +#define LBSI_SOFT_RESET (1<<1) +#define LMI_SOFT_RESET (1<<2) +#define VCPU_SOFT_RESET (1<<3) +#define CSM_SOFT_RESET (1<<5) +#define CXW_SOFT_RESET (1<<6) +#define TAP_SOFT_RESET (1<<7) +#define LMI_UMC_SOFT_RESET (1<<13) +#define UVD_RBC_IB_BASE 0xf684 +#define UVD_RBC_IB_SIZE 0xf688 +#define UVD_RBC_RB_BASE 0xf68c +#define UVD_RBC_RB_RPTR 0xf690 +#define UVD_RBC_RB_WPTR 0xf694 +#define UVD_RBC_RB_WPTR_CNTL 0xf698 + +#define UVD_STATUS 0xf6bc + +#define UVD_SEMA_TIMEOUT_STATUS 0xf6c0 +#define UVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL 0xf6c4 +#define UVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL 0xf6c8 +#define UVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL 0xf6cc + +#define UVD_RBC_RB_CNTL 0xf6a4 +#define UVD_RBC_RB_RPTR_ADDR 0xf6a8 + +#define UVD_CONTEXT_ID 0xf6f4 + +/* * PM4 */ #define PACKET0(reg, n) ((RADEON_PACKET_TYPE0 << 30) | \ diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 8263af3..18904fb 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -95,6 +95,7 @@ extern int radeon_hw_i2c; extern int radeon_pcie_gen2; extern int radeon_msi; extern int radeon_lockup_timeout; +extern int radeon_fastfb; /* * Copy from radeon_drv.h so we don't have to include both and have conflicting @@ -109,24 +110,27 @@ extern int radeon_lockup_timeout; #define RADEON_BIOS_NUM_SCRATCH 8 /* max number of rings */ -#define RADEON_NUM_RINGS 5 +#define RADEON_NUM_RINGS 6 /* fence seq are set to this number when signaled */ #define RADEON_FENCE_SIGNALED_SEQ 0LL /* internal ring indices */ /* r1xx+ has gfx CP ring */ -#define RADEON_RING_TYPE_GFX_INDEX 0 +#define RADEON_RING_TYPE_GFX_INDEX 0 /* cayman has 2 compute CP rings */ -#define CAYMAN_RING_TYPE_CP1_INDEX 1 -#define CAYMAN_RING_TYPE_CP2_INDEX 2 +#define CAYMAN_RING_TYPE_CP1_INDEX 1 +#define CAYMAN_RING_TYPE_CP2_INDEX 2 /* R600+ has an async dma ring */ #define R600_RING_TYPE_DMA_INDEX 3 /* cayman add a second async dma ring */ #define CAYMAN_RING_TYPE_DMA1_INDEX 4 +/* R600+ */ +#define R600_RING_TYPE_UVD_INDEX 5 + /* hardcode those limit for now */ #define RADEON_VA_IB_OFFSET (1 << 20) #define RADEON_VA_RESERVED_SIZE (8 << 20) @@ -202,6 +206,11 @@ void radeon_pm_suspend(struct radeon_device *rdev); void radeon_pm_resume(struct radeon_device *rdev); void radeon_combios_get_power_modes(struct radeon_device *rdev); void radeon_atombios_get_power_modes(struct radeon_device *rdev); +int radeon_atom_get_clock_dividers(struct radeon_device *rdev, + u8 clock_type, + u32 clock, + bool strobe_mode, + struct atom_clock_dividers *dividers); void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type); void rs690_pm_info(struct radeon_device *rdev); extern int rv6xx_get_temp(struct radeon_device *rdev); @@ -357,8 +366,9 @@ struct radeon_bo_list { struct ttm_validate_buffer tv; struct radeon_bo *bo; uint64_t gpu_offset; - unsigned rdomain; - unsigned wdomain; + bool written; + unsigned domain; + unsigned alt_domain; u32 tiling_flags; }; @@ -517,6 +527,7 @@ struct radeon_mc { bool vram_is_ddr; bool igp_sideport_enabled; u64 gtt_base_align; + u64 mc_mask; }; bool radeon_combios_sideport_present(struct radeon_device *rdev); @@ -918,6 +929,7 @@ struct radeon_wb { #define R600_WB_DMA_RPTR_OFFSET 1792 #define R600_WB_IH_WPTR_OFFSET 2048 #define CAYMAN_WB_DMA1_RPTR_OFFSET 2304 +#define R600_WB_UVD_RPTR_OFFSET 2560 #define R600_WB_EVENT_OFFSET 3072 /** @@ -1118,6 +1130,33 @@ struct radeon_pm { int radeon_pm_get_type_index(struct radeon_device *rdev, enum radeon_pm_state_type ps_type, int instance); +/* + * UVD + */ +#define RADEON_MAX_UVD_HANDLES 10 +#define RADEON_UVD_STACK_SIZE (1024*1024) +#define RADEON_UVD_HEAP_SIZE (1024*1024) + +struct radeon_uvd { + struct radeon_bo *vcpu_bo; + void *cpu_addr; + uint64_t gpu_addr; + atomic_t handles[RADEON_MAX_UVD_HANDLES]; + struct drm_file *filp[RADEON_MAX_UVD_HANDLES]; +}; + +int radeon_uvd_init(struct radeon_device *rdev); +void radeon_uvd_fini(struct radeon_device *rdev); +int radeon_uvd_suspend(struct radeon_device *rdev); +int radeon_uvd_resume(struct radeon_device *rdev); +int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, + uint32_t handle, struct radeon_fence **fence); +int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, + uint32_t handle, struct radeon_fence **fence); +void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo); +void radeon_uvd_free_handles(struct radeon_device *rdev, + struct drm_file *filp); +int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); struct r600_audio { int channels; @@ -1281,6 +1320,7 @@ struct radeon_asic { int (*get_pcie_lanes)(struct radeon_device *rdev); void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes); void (*set_clock_gating)(struct radeon_device *rdev, int enable); + int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk); } pm; /* pageflipping */ struct { @@ -1443,6 +1483,7 @@ struct si_asic { unsigned multi_gpu_tile_size; unsigned tile_config; + uint32_t tile_mode_array[32]; }; union radeon_asic_config { @@ -1608,6 +1649,7 @@ struct radeon_device { struct radeon_asic *asic; struct radeon_gem gem; struct radeon_pm pm; + struct radeon_uvd uvd; uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH]; struct radeon_wb wb; struct radeon_dummy_page dummy_page; @@ -1615,12 +1657,14 @@ struct radeon_device { bool suspend; bool need_dma32; bool accel_working; + bool fastfb_working; /* IGP feature*/ struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES]; const struct firmware *me_fw; /* all family ME firmware */ const struct firmware *pfp_fw; /* r6/700 PFP firmware */ const struct firmware *rlc_fw; /* r6/700 RLC firmware */ const struct firmware *mc_fw; /* NI MC firmware */ const struct firmware *ce_fw; /* SI CE firmware */ + const struct firmware *uvd_fw; /* UVD firmware */ struct r600_blit r600_blit; struct r600_vram_scratch vram_scratch; int msi_enabled; /* msi enabled */ @@ -1688,8 +1732,8 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); #define WREG32_MC(reg, v) rdev->mc_wreg(rdev, (reg), (v)) #define RREG32_PCIE(reg) rv370_pcie_rreg(rdev, (reg)) #define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v)) -#define RREG32_PCIE_P(reg) rdev->pciep_rreg(rdev, (reg)) -#define WREG32_PCIE_P(reg, v) rdev->pciep_wreg(rdev, (reg), (v)) +#define RREG32_PCIE_PORT(reg) rdev->pciep_rreg(rdev, (reg)) +#define WREG32_PCIE_PORT(reg, v) rdev->pciep_wreg(rdev, (reg), (v)) #define WREG32_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32(reg); \ @@ -1845,6 +1889,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_get_pcie_lanes(rdev) (rdev)->asic->pm.get_pcie_lanes((rdev)) #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->pm.set_pcie_lanes((rdev), (l)) #define radeon_set_clock_gating(rdev, e) (rdev)->asic->pm.set_clock_gating((rdev), (e)) +#define radeon_set_uvd_clocks(rdev, v, d) (rdev)->asic->pm.set_uvd_clocks((rdev), (v), (d)) #define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->surface.set_reg((rdev), (r), (f), (p), (o), (s))) #define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->surface.clear_reg((rdev), (r))) #define radeon_bandwidth_update(rdev) (rdev)->asic->display.bandwidth_update((rdev)) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index aba0a89..48d0200 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1130,6 +1130,15 @@ static struct radeon_asic rv770_asic = { .ring_test = &r600_dma_ring_test, .ib_test = &r600_dma_ib_test, .is_lockup = &r600_dma_is_lockup, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &r600_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1174,6 +1183,7 @@ static struct radeon_asic rv770_asic = { .get_pcie_lanes = &r600_get_pcie_lanes, .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, + .set_uvd_clocks = &rv770_set_uvd_clocks, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, @@ -1216,6 +1226,15 @@ static struct radeon_asic evergreen_asic = { .ring_test = &r600_dma_ring_test, .ib_test = &r600_dma_ib_test, .is_lockup = &evergreen_dma_is_lockup, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &r600_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1260,6 +1279,7 @@ static struct radeon_asic evergreen_asic = { .get_pcie_lanes = &r600_get_pcie_lanes, .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, + .set_uvd_clocks = &evergreen_set_uvd_clocks, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1302,6 +1322,15 @@ static struct radeon_asic sumo_asic = { .ring_test = &r600_dma_ring_test, .ib_test = &r600_dma_ib_test, .is_lockup = &evergreen_dma_is_lockup, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &r600_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1346,6 +1375,7 @@ static struct radeon_asic sumo_asic = { .get_pcie_lanes = NULL, .set_pcie_lanes = NULL, .set_clock_gating = NULL, + .set_uvd_clocks = &sumo_set_uvd_clocks, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1388,6 +1418,15 @@ static struct radeon_asic btc_asic = { .ring_test = &r600_dma_ring_test, .ib_test = &r600_dma_ib_test, .is_lockup = &evergreen_dma_is_lockup, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &r600_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1429,9 +1468,10 @@ static struct radeon_asic btc_asic = { .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, - .get_pcie_lanes = NULL, - .set_pcie_lanes = NULL, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, + .set_uvd_clocks = &evergreen_set_uvd_clocks, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1517,6 +1557,15 @@ static struct radeon_asic cayman_asic = { .ib_test = &r600_dma_ib_test, .is_lockup = &cayman_dma_is_lockup, .vm_flush = &cayman_dma_vm_flush, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &cayman_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1558,9 +1607,10 @@ static struct radeon_asic cayman_asic = { .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, - .get_pcie_lanes = NULL, - .set_pcie_lanes = NULL, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, + .set_uvd_clocks = &evergreen_set_uvd_clocks, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1646,6 +1696,15 @@ static struct radeon_asic trinity_asic = { .ib_test = &r600_dma_ib_test, .is_lockup = &cayman_dma_is_lockup, .vm_flush = &cayman_dma_vm_flush, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &cayman_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1690,6 +1749,7 @@ static struct radeon_asic trinity_asic = { .get_pcie_lanes = NULL, .set_pcie_lanes = NULL, .set_clock_gating = NULL, + .set_uvd_clocks = &sumo_set_uvd_clocks, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1775,6 +1835,15 @@ static struct radeon_asic si_asic = { .ib_test = &r600_dma_ib_test, .is_lockup = &si_dma_is_lockup, .vm_flush = &si_dma_vm_flush, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &cayman_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, } }, .irq = { @@ -1816,9 +1885,10 @@ static struct radeon_asic si_asic = { .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, - .get_pcie_lanes = NULL, - .set_pcie_lanes = NULL, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, + .set_uvd_clocks = &si_set_uvd_clocks, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 3535f73..2add526 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -330,6 +330,7 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); int r600_dma_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); +int r600_uvd_ring_test(struct radeon_device *rdev, struct radeon_ring *ring); int r600_copy_blit(struct radeon_device *rdev, uint64_t src_offset, uint64_t dst_offset, unsigned num_gpu_pages, struct radeon_fence **fence); @@ -392,6 +393,19 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); +/* uvd */ +int r600_uvd_init(struct radeon_device *rdev); +int r600_uvd_rbc_start(struct radeon_device *rdev); +void r600_uvd_rbc_stop(struct radeon_device *rdev); +int r600_uvd_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); +void r600_uvd_fence_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void r600_uvd_semaphore_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait); +void r600_uvd_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); + /* * rv770,rv730,rv710,rv740 */ @@ -409,6 +423,8 @@ int rv770_copy_dma(struct radeon_device *rdev, unsigned num_gpu_pages, struct radeon_fence **fence); u32 rv770_get_xclk(struct radeon_device *rdev); +int rv770_uvd_resume(struct radeon_device *rdev); +int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); /* * evergreen @@ -444,6 +460,8 @@ extern void evergreen_pm_prepare(struct radeon_device *rdev); extern void evergreen_pm_finish(struct radeon_device *rdev); extern void sumo_pm_init_profile(struct radeon_device *rdev); extern void btc_pm_init_profile(struct radeon_device *rdev); +int sumo_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); +int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc); extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base); extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc); @@ -465,6 +483,10 @@ int evergreen_copy_dma(struct radeon_device *rdev, */ void cayman_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence); +void cayman_uvd_semaphore_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait); void cayman_pcie_gart_tlb_flush(struct radeon_device *rdev); int cayman_init(struct radeon_device *rdev); void cayman_fini(struct radeon_device *rdev); @@ -524,5 +546,6 @@ int si_copy_dma(struct radeon_device *rdev, void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); u32 si_get_xclk(struct radeon_device *rdev); uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); +int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); #endif diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index f22eb57..8c1779c 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2654,6 +2654,113 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) rdev->pm.current_vddc = 0; } +union get_clock_dividers { + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS v1; + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 v2; + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 v3; + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 v4; + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 v5; +}; + +int radeon_atom_get_clock_dividers(struct radeon_device *rdev, + u8 clock_type, + u32 clock, + bool strobe_mode, + struct atom_clock_dividers *dividers) +{ + union get_clock_dividers args; + int index = GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL); + u8 frev, crev; + + memset(&args, 0, sizeof(args)); + memset(dividers, 0, sizeof(struct atom_clock_dividers)); + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return -EINVAL; + + switch (crev) { + case 1: + /* r4xx, r5xx */ + args.v1.ucAction = clock_type; + args.v1.ulClock = cpu_to_le32(clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + dividers->post_div = args.v1.ucPostDiv; + dividers->fb_div = args.v1.ucFbDiv; + dividers->enable_post_div = true; + break; + case 2: + case 3: + /* r6xx, r7xx, evergreen, ni */ + if (rdev->family <= CHIP_RV770) { + args.v2.ucAction = clock_type; + args.v2.ulClock = cpu_to_le32(clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + dividers->post_div = args.v2.ucPostDiv; + dividers->fb_div = le16_to_cpu(args.v2.usFbDiv); + dividers->ref_div = args.v2.ucAction; + if (rdev->family == CHIP_RV770) { + dividers->enable_post_div = (le32_to_cpu(args.v2.ulClock) & (1 << 24)) ? + true : false; + dividers->vco_mode = (le32_to_cpu(args.v2.ulClock) & (1 << 25)) ? 1 : 0; + } else + dividers->enable_post_div = (dividers->fb_div & 1) ? true : false; + } else { + if (clock_type == COMPUTE_ENGINE_PLL_PARAM) { + args.v3.ulClock.ulComputeClockFlag = clock_type; + args.v3.ulClock.ulClockFreq = cpu_to_le32(clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + dividers->post_div = args.v3.ucPostDiv; + dividers->enable_post_div = (args.v3.ucCntlFlag & + ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN) ? true : false; + dividers->enable_dithen = (args.v3.ucCntlFlag & + ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE) ? false : true; + dividers->fb_div = le16_to_cpu(args.v3.ulFbDiv.usFbDiv); + dividers->frac_fb_div = le16_to_cpu(args.v3.ulFbDiv.usFbDivFrac); + dividers->ref_div = args.v3.ucRefDiv; + dividers->vco_mode = (args.v3.ucCntlFlag & + ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0; + } else { + args.v5.ulClock.ulComputeClockFlag = clock_type; + args.v5.ulClock.ulClockFreq = cpu_to_le32(clock); /* 10 khz */ + if (strobe_mode) + args.v5.ucInputFlag = ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + dividers->post_div = args.v5.ucPostDiv; + dividers->enable_post_div = (args.v5.ucCntlFlag & + ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN) ? true : false; + dividers->enable_dithen = (args.v5.ucCntlFlag & + ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE) ? false : true; + dividers->whole_fb_div = le16_to_cpu(args.v5.ulFbDiv.usFbDiv); + dividers->frac_fb_div = le16_to_cpu(args.v5.ulFbDiv.usFbDivFrac); + dividers->ref_div = args.v5.ucRefDiv; + dividers->vco_mode = (args.v5.ucCntlFlag & + ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0; + } + } + break; + case 4: + /* fusion */ + args.v4.ulClock = cpu_to_le32(clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + dividers->post_div = args.v4.ucPostDiv; + dividers->real_clock = le32_to_cpu(args.v4.ulClock); + break; + default: + return -EINVAL; + } + return 0; +} + void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable) { DYNAMIC_CLOCK_GATING_PS_ALLOCATION args; diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index bedda9c..6e05a2e 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -122,10 +122,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, goto out_cleanup; } - /* r100 doesn't have dma engine so skip the test */ - /* also, VRAM-to-VRAM test doesn't make much sense for DMA */ - /* skip it as well if domains are the same */ - if ((rdev->asic->copy.dma) && (sdomain != ddomain)) { + if (rdev->asic->copy.dma) { time = radeon_benchmark_do_move(rdev, size, saddr, daddr, RADEON_BENCHMARK_COPY_DMA, n); if (time < 0) @@ -135,13 +132,15 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, sdomain, ddomain, "dma"); } - time = radeon_benchmark_do_move(rdev, size, saddr, daddr, - RADEON_BENCHMARK_COPY_BLIT, n); - if (time < 0) - goto out_cleanup; - if (time > 0) - radeon_benchmark_log_results(n, size, time, - sdomain, ddomain, "blit"); + if (rdev->asic->copy.blit) { + time = radeon_benchmark_do_move(rdev, size, saddr, daddr, + RADEON_BENCHMARK_COPY_BLIT, n); + if (time < 0) + goto out_cleanup; + if (time > 0) + radeon_benchmark_log_results(n, size, time, + sdomain, ddomain, "blit"); + } out_cleanup: if (sobj) { diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 3e403bd..78edadc 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -970,6 +970,15 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct found = 1; } + /* quirks */ + /* Radeon 9100 (R200) */ + if ((dev->pdev->device == 0x514D) && + (dev->pdev->subsystem_vendor == 0x174B) && + (dev->pdev->subsystem_device == 0x7149)) { + /* vbios value is bad, use the default */ + found = 0; + } + if (!found) /* fallback to defaults */ radeon_legacy_get_primary_dac_info_from_table(rdev, p_dac); diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 70d3824..c740707 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -63,30 +63,50 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) break; } } - if (!duplicate) { - p->relocs[i].gobj = drm_gem_object_lookup(ddev, - p->filp, - r->handle); - if (p->relocs[i].gobj == NULL) { - DRM_ERROR("gem object lookup failed 0x%x\n", - r->handle); - return -ENOENT; - } - p->relocs_ptr[i] = &p->relocs[i]; - p->relocs[i].robj = gem_to_radeon_bo(p->relocs[i].gobj); - p->relocs[i].lobj.bo = p->relocs[i].robj; - p->relocs[i].lobj.wdomain = r->write_domain; - p->relocs[i].lobj.rdomain = r->read_domains; - p->relocs[i].lobj.tv.bo = &p->relocs[i].robj->tbo; - p->relocs[i].handle = r->handle; - p->relocs[i].flags = r->flags; - radeon_bo_list_add_object(&p->relocs[i].lobj, - &p->validated); - - } else + if (duplicate) { p->relocs[i].handle = 0; + continue; + } + + p->relocs[i].gobj = drm_gem_object_lookup(ddev, p->filp, + r->handle); + if (p->relocs[i].gobj == NULL) { + DRM_ERROR("gem object lookup failed 0x%x\n", + r->handle); + return -ENOENT; + } + p->relocs_ptr[i] = &p->relocs[i]; + p->relocs[i].robj = gem_to_radeon_bo(p->relocs[i].gobj); + p->relocs[i].lobj.bo = p->relocs[i].robj; + p->relocs[i].lobj.written = !!r->write_domain; + + /* the first reloc of an UVD job is the + msg and that must be in VRAM */ + if (p->ring == R600_RING_TYPE_UVD_INDEX && i == 0) { + /* TODO: is this still needed for NI+ ? */ + p->relocs[i].lobj.domain = + RADEON_GEM_DOMAIN_VRAM; + + p->relocs[i].lobj.alt_domain = + RADEON_GEM_DOMAIN_VRAM; + + } else { + uint32_t domain = r->write_domain ? + r->write_domain : r->read_domains; + + p->relocs[i].lobj.domain = domain; + if (domain == RADEON_GEM_DOMAIN_VRAM) + domain |= RADEON_GEM_DOMAIN_GTT; + p->relocs[i].lobj.alt_domain = domain; + } + + p->relocs[i].lobj.tv.bo = &p->relocs[i].robj->tbo; + p->relocs[i].handle = r->handle; + + radeon_bo_list_add_object(&p->relocs[i].lobj, + &p->validated); } - return radeon_bo_list_validate(&p->validated); + return radeon_bo_list_validate(&p->validated, p->ring); } static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority) @@ -121,6 +141,9 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority return -EINVAL; } break; + case RADEON_CS_RING_UVD: + p->ring = R600_RING_TYPE_UVD_INDEX; + break; } return 0; } @@ -241,15 +264,15 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) return -EINVAL; } - /* we only support VM on SI+ */ - if ((p->rdev->family >= CHIP_TAHITI) && - ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { - DRM_ERROR("VM required on SI+!\n"); + if (radeon_cs_get_ring(p, ring, priority)) return -EINVAL; - } - if (radeon_cs_get_ring(p, ring, priority)) + /* we only support VM on some SI+ rings */ + if ((p->rdev->asic->ring[p->ring].cs_parse == NULL) && + ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { + DRM_ERROR("Ring %d requires VM!\n", p->ring); return -EINVAL; + } } /* deal with non-vm */ diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 44b8034..62d0ba3 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -359,7 +359,7 @@ void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 uint64_t limit = (uint64_t)radeon_vram_limit << 20; mc->vram_start = base; - if (mc->mc_vram_size > (0xFFFFFFFF - base + 1)) { + if (mc->mc_vram_size > (rdev->mc.mc_mask - base + 1)) { dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n"); mc->real_vram_size = mc->aper_size; mc->mc_vram_size = mc->aper_size; @@ -394,7 +394,7 @@ void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) { u64 size_af, size_bf; - size_af = ((0xFFFFFFFF - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; + size_af = ((rdev->mc.mc_mask - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; size_bf = mc->vram_start & ~mc->gtt_base_align; if (size_bf > size_af) { if (mc->gtt_size > size_bf) { @@ -1068,6 +1068,17 @@ int radeon_device_init(struct radeon_device *rdev, radeon_agp_disable(rdev); } + /* Set the internal MC address mask + * This is the max address of the GPU's + * internal address space. + */ + if (rdev->family >= CHIP_CAYMAN) + rdev->mc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + else if (rdev->family >= CHIP_CEDAR) + rdev->mc.mc_mask = 0xfffffffffULL; /* 36 bit MC */ + else + rdev->mc.mc_mask = 0xffffffffULL; /* 32 bit MC */ + /* set DMA mask + need_dma32 flags. * PCIE - can handle 40-bits. * IGP - can handle 40-bits diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 1677584..d33f484 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -70,9 +70,13 @@ * 2.27.0 - r600-SI: Add CS ioctl support for async DMA * 2.28.0 - r600-eg: Add MEM_WRITE packet support * 2.29.0 - R500 FP16 color clear registers + * 2.30.0 - fix for FMASK texturing + * 2.31.0 - Add fastfb support for rs690 + * 2.32.0 - new info request for rings working + * 2.33.0 - Add SI tiling mode array query */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 29 +#define KMS_DRIVER_MINOR 33 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); @@ -159,6 +163,7 @@ int radeon_hw_i2c = 0; int radeon_pcie_gen2 = -1; int radeon_msi = -1; int radeon_lockup_timeout = 10000; +int radeon_fastfb = 0; MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); module_param_named(no_wb, radeon_no_wb, int, 0444); @@ -211,6 +216,9 @@ module_param_named(msi, radeon_msi, int, 0444); MODULE_PARM_DESC(lockup_timeout, "GPU lockup timeout in ms (defaul 10000 = 10 seconds, 0 = disable)"); module_param_named(lockup_timeout, radeon_lockup_timeout, int, 0444); +MODULE_PARM_DESC(fastfb, "Direct FB access for IGP chips (0 = disable, 1 = enable)"); +module_param_named(fastfb, radeon_fastfb, int, 0444); + static struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 3435625..82fe183 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -31,9 +31,9 @@ #include <linux/seq_file.h> #include <linux/atomic.h> #include <linux/wait.h> -#include <linux/list.h> #include <linux/kref.h> #include <linux/slab.h> +#include <linux/firmware.h> #include <drm/drmP.h> #include "radeon_reg.h" #include "radeon.h" @@ -767,8 +767,21 @@ int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); if (rdev->wb.use_event || !radeon_ring_supports_scratch_reg(rdev, &rdev->ring[ring])) { - rdev->fence_drv[ring].scratch_reg = 0; - index = R600_WB_EVENT_OFFSET + ring * 4; + if (ring != R600_RING_TYPE_UVD_INDEX) { + rdev->fence_drv[ring].scratch_reg = 0; + index = R600_WB_EVENT_OFFSET + ring * 4; + rdev->fence_drv[ring].cpu_addr = &rdev->wb.wb[index/4]; + rdev->fence_drv[ring].gpu_addr = rdev->wb.gpu_addr + + index; + + } else { + /* put fence directly behind firmware */ + rdev->fence_drv[ring].cpu_addr = rdev->uvd.cpu_addr + + rdev->uvd_fw->size; + rdev->fence_drv[ring].gpu_addr = rdev->uvd.gpu_addr + + rdev->uvd_fw->size; + } + } else { r = radeon_scratch_get(rdev, &rdev->fence_drv[ring].scratch_reg); if (r) { @@ -778,9 +791,9 @@ int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv[ring].scratch_reg - rdev->scratch.reg_base; + rdev->fence_drv[ring].cpu_addr = &rdev->wb.wb[index/4]; + rdev->fence_drv[ring].gpu_addr = rdev->wb.gpu_addr + index; } - rdev->fence_drv[ring].cpu_addr = &rdev->wb.wb[index/4]; - rdev->fence_drv[ring].gpu_addr = rdev->wb.gpu_addr + index; radeon_fence_write(rdev, atomic64_read(&rdev->fence_drv[ring].last_seq), ring); rdev->fence_drv[ring].initialized = true; dev_info(rdev->dev, "fence driver on ring %d use gpu addr 0x%016llx and cpu addr 0x%p\n", diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 90374dd..48f80cd 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -400,6 +400,9 @@ void radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block) { unsigned long irqflags; + if (!rdev->ddev->irq_enabled) + return; + spin_lock_irqsave(&rdev->irq.lock, irqflags); rdev->irq.afmt[block] = true; radeon_irq_set(rdev); @@ -419,6 +422,9 @@ void radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block) { unsigned long irqflags; + if (!rdev->ddev->irq_enabled) + return; + spin_lock_irqsave(&rdev->irq.lock, irqflags); rdev->irq.afmt[block] = false; radeon_irq_set(rdev); @@ -438,6 +444,9 @@ void radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask) unsigned long irqflags; int i; + if (!rdev->ddev->irq_enabled) + return; + spin_lock_irqsave(&rdev->irq.lock, irqflags); for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) rdev->irq.hpd[i] |= !!(hpd_mask & (1 << i)); @@ -458,6 +467,9 @@ void radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask) unsigned long irqflags; int i; + if (!rdev->ddev->irq_enabled) + return; + spin_lock_irqsave(&rdev->irq.lock, irqflags); for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) rdev->irq.hpd[i] &= !(hpd_mask & (1 << i)); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index c75cb2c..4f2d4f4 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -50,9 +50,13 @@ int radeon_driver_unload_kms(struct drm_device *dev) if (rdev == NULL) return 0; + if (rdev->rmmio == NULL) + goto done_free; radeon_acpi_fini(rdev); radeon_modeset_fini(rdev); radeon_device_fini(rdev); + +done_free: kfree(rdev); dev->dev_private = NULL; return 0; @@ -176,80 +180,65 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) struct radeon_device *rdev = dev->dev_private; struct drm_radeon_info *info = data; struct radeon_mode_info *minfo = &rdev->mode_info; - uint32_t value, *value_ptr; - uint64_t value64, *value_ptr64; + uint32_t *value, value_tmp, *value_ptr, value_size; + uint64_t value64; struct drm_crtc *crtc; int i, found; - /* TIMESTAMP is a 64-bit value, needs special handling. */ - if (info->request == RADEON_INFO_TIMESTAMP) { - if (rdev->family >= CHIP_R600) { - value_ptr64 = (uint64_t*)((unsigned long)info->value); - value64 = radeon_get_gpu_clock_counter(rdev); - - if (DRM_COPY_TO_USER(value_ptr64, &value64, sizeof(value64))) { - DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); - return -EFAULT; - } - return 0; - } else { - DRM_DEBUG_KMS("timestamp is r6xx+ only!\n"); - return -EINVAL; - } - } - value_ptr = (uint32_t *)((unsigned long)info->value); - if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) { - DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); - return -EFAULT; - } + value = &value_tmp; + value_size = sizeof(uint32_t); switch (info->request) { case RADEON_INFO_DEVICE_ID: - value = dev->pci_device; + *value = dev->pci_device; break; case RADEON_INFO_NUM_GB_PIPES: - value = rdev->num_gb_pipes; + *value = rdev->num_gb_pipes; break; case RADEON_INFO_NUM_Z_PIPES: - value = rdev->num_z_pipes; + *value = rdev->num_z_pipes; break; case RADEON_INFO_ACCEL_WORKING: /* xf86-video-ati 6.13.0 relies on this being false for evergreen */ if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) - value = false; + *value = false; else - value = rdev->accel_working; + *value = rdev->accel_working; break; case RADEON_INFO_CRTC_FROM_ID: + if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } for (i = 0, found = 0; i < rdev->num_crtc; i++) { crtc = (struct drm_crtc *)minfo->crtcs[i]; - if (crtc && crtc->base.id == value) { + if (crtc && crtc->base.id == *value) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - value = radeon_crtc->crtc_id; + *value = radeon_crtc->crtc_id; found = 1; break; } } if (!found) { - DRM_DEBUG_KMS("unknown crtc id %d\n", value); + DRM_DEBUG_KMS("unknown crtc id %d\n", *value); return -EINVAL; } break; case RADEON_INFO_ACCEL_WORKING2: - value = rdev->accel_working; + *value = rdev->accel_working; break; case RADEON_INFO_TILING_CONFIG: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.tile_config; + *value = rdev->config.si.tile_config; else if (rdev->family >= CHIP_CAYMAN) - value = rdev->config.cayman.tile_config; + *value = rdev->config.cayman.tile_config; else if (rdev->family >= CHIP_CEDAR) - value = rdev->config.evergreen.tile_config; + *value = rdev->config.evergreen.tile_config; else if (rdev->family >= CHIP_RV770) - value = rdev->config.rv770.tile_config; + *value = rdev->config.rv770.tile_config; else if (rdev->family >= CHIP_R600) - value = rdev->config.r600.tile_config; + *value = rdev->config.r600.tile_config; else { DRM_DEBUG_KMS("tiling config is r6xx+ only!\n"); return -EINVAL; @@ -262,73 +251,81 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) * * When returning, the value is 1 if filp owns hyper-z access, * 0 otherwise. */ - if (value >= 2) { - DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", value); + if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + if (*value >= 2) { + DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", *value); return -EINVAL; } - radeon_set_filp_rights(dev, &rdev->hyperz_filp, filp, &value); + radeon_set_filp_rights(dev, &rdev->hyperz_filp, filp, value); break; case RADEON_INFO_WANT_CMASK: /* The same logic as Hyper-Z. */ - if (value >= 2) { - DRM_DEBUG_KMS("WANT_CMASK: invalid value %d\n", value); + if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + if (*value >= 2) { + DRM_DEBUG_KMS("WANT_CMASK: invalid value %d\n", *value); return -EINVAL; } - radeon_set_filp_rights(dev, &rdev->cmask_filp, filp, &value); + radeon_set_filp_rights(dev, &rdev->cmask_filp, filp, value); break; case RADEON_INFO_CLOCK_CRYSTAL_FREQ: /* return clock value in KHz */ if (rdev->asic->get_xclk) - value = radeon_get_xclk(rdev) * 10; + *value = radeon_get_xclk(rdev) * 10; else - value = rdev->clock.spll.reference_freq * 10; + *value = rdev->clock.spll.reference_freq * 10; break; case RADEON_INFO_NUM_BACKENDS: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.max_backends_per_se * + *value = rdev->config.si.max_backends_per_se * rdev->config.si.max_shader_engines; else if (rdev->family >= CHIP_CAYMAN) - value = rdev->config.cayman.max_backends_per_se * + *value = rdev->config.cayman.max_backends_per_se * rdev->config.cayman.max_shader_engines; else if (rdev->family >= CHIP_CEDAR) - value = rdev->config.evergreen.max_backends; + *value = rdev->config.evergreen.max_backends; else if (rdev->family >= CHIP_RV770) - value = rdev->config.rv770.max_backends; + *value = rdev->config.rv770.max_backends; else if (rdev->family >= CHIP_R600) - value = rdev->config.r600.max_backends; + *value = rdev->config.r600.max_backends; else { return -EINVAL; } break; case RADEON_INFO_NUM_TILE_PIPES: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.max_tile_pipes; + *value = rdev->config.si.max_tile_pipes; else if (rdev->family >= CHIP_CAYMAN) - value = rdev->config.cayman.max_tile_pipes; + *value = rdev->config.cayman.max_tile_pipes; else if (rdev->family >= CHIP_CEDAR) - value = rdev->config.evergreen.max_tile_pipes; + *value = rdev->config.evergreen.max_tile_pipes; else if (rdev->family >= CHIP_RV770) - value = rdev->config.rv770.max_tile_pipes; + *value = rdev->config.rv770.max_tile_pipes; else if (rdev->family >= CHIP_R600) - value = rdev->config.r600.max_tile_pipes; + *value = rdev->config.r600.max_tile_pipes; else { return -EINVAL; } break; case RADEON_INFO_FUSION_GART_WORKING: - value = 1; + *value = 1; break; case RADEON_INFO_BACKEND_MAP: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.backend_map; + *value = rdev->config.si.backend_map; else if (rdev->family >= CHIP_CAYMAN) - value = rdev->config.cayman.backend_map; + *value = rdev->config.cayman.backend_map; else if (rdev->family >= CHIP_CEDAR) - value = rdev->config.evergreen.backend_map; + *value = rdev->config.evergreen.backend_map; else if (rdev->family >= CHIP_RV770) - value = rdev->config.rv770.backend_map; + *value = rdev->config.rv770.backend_map; else if (rdev->family >= CHIP_R600) - value = rdev->config.r600.backend_map; + *value = rdev->config.r600.backend_map; else { return -EINVAL; } @@ -337,50 +334,91 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) /* this is where we report if vm is supported or not */ if (rdev->family < CHIP_CAYMAN) return -EINVAL; - value = RADEON_VA_RESERVED_SIZE; + *value = RADEON_VA_RESERVED_SIZE; break; case RADEON_INFO_IB_VM_MAX_SIZE: /* this is where we report if vm is supported or not */ if (rdev->family < CHIP_CAYMAN) return -EINVAL; - value = RADEON_IB_VM_MAX_SIZE; + *value = RADEON_IB_VM_MAX_SIZE; break; case RADEON_INFO_MAX_PIPES: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.max_cu_per_sh; + *value = rdev->config.si.max_cu_per_sh; else if (rdev->family >= CHIP_CAYMAN) - value = rdev->config.cayman.max_pipes_per_simd; + *value = rdev->config.cayman.max_pipes_per_simd; else if (rdev->family >= CHIP_CEDAR) - value = rdev->config.evergreen.max_pipes; + *value = rdev->config.evergreen.max_pipes; else if (rdev->family >= CHIP_RV770) - value = rdev->config.rv770.max_pipes; + *value = rdev->config.rv770.max_pipes; else if (rdev->family >= CHIP_R600) - value = rdev->config.r600.max_pipes; + *value = rdev->config.r600.max_pipes; else { return -EINVAL; } break; + case RADEON_INFO_TIMESTAMP: + if (rdev->family < CHIP_R600) { + DRM_DEBUG_KMS("timestamp is r6xx+ only!\n"); + return -EINVAL; + } + value = (uint32_t*)&value64; + value_size = sizeof(uint64_t); + value64 = radeon_get_gpu_clock_counter(rdev); + break; case RADEON_INFO_MAX_SE: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.max_shader_engines; + *value = rdev->config.si.max_shader_engines; else if (rdev->family >= CHIP_CAYMAN) - value = rdev->config.cayman.max_shader_engines; + *value = rdev->config.cayman.max_shader_engines; else if (rdev->family >= CHIP_CEDAR) - value = rdev->config.evergreen.num_ses; + *value = rdev->config.evergreen.num_ses; else - value = 1; + *value = 1; break; case RADEON_INFO_MAX_SH_PER_SE: if (rdev->family >= CHIP_TAHITI) - value = rdev->config.si.max_sh_per_se; + *value = rdev->config.si.max_sh_per_se; else return -EINVAL; break; + case RADEON_INFO_FASTFB_WORKING: + *value = rdev->fastfb_working; + break; + case RADEON_INFO_RING_WORKING: + if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + switch (*value) { + case RADEON_CS_RING_GFX: + case RADEON_CS_RING_COMPUTE: + *value = rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready; + break; + case RADEON_CS_RING_DMA: + *value = rdev->ring[R600_RING_TYPE_DMA_INDEX].ready; + *value |= rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX].ready; + break; + case RADEON_CS_RING_UVD: + *value = rdev->ring[R600_RING_TYPE_UVD_INDEX].ready; + break; + default: + return -EINVAL; + } + break; + case RADEON_INFO_SI_TILE_MODE_ARRAY: + if (rdev->family < CHIP_TAHITI) { + DRM_DEBUG_KMS("tile mode array is si only!\n"); + return -EINVAL; + } + value = rdev->config.si.tile_mode_array; + value_size = sizeof(uint32_t)*32; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; } - if (DRM_COPY_TO_USER(value_ptr, &value, sizeof(uint32_t))) { + if (DRM_COPY_TO_USER(value_ptr, (char*)value, value_size)) { DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); return -EFAULT; } @@ -513,6 +551,7 @@ void radeon_driver_preclose_kms(struct drm_device *dev, rdev->hyperz_filp = NULL; if (rdev->cmask_filp == file_priv) rdev->cmask_filp = NULL; + radeon_uvd_free_handles(rdev, file_priv); } /* diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 4003f5a..44e579e 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -492,6 +492,29 @@ struct radeon_framebuffer { #define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \ ((em) == ATOM_ENCODER_MODE_DP_MST)) +struct atom_clock_dividers { + u32 post_div; + union { + struct { +#ifdef __BIG_ENDIAN + u32 reserved : 6; + u32 whole_fb_div : 12; + u32 frac_fb_div : 14; +#else + u32 frac_fb_div : 14; + u32 whole_fb_div : 12; + u32 reserved : 6; +#endif + }; + u32 fb_div; + }; + u32 ref_div; + bool enable_post_div; + bool enable_dithen; + u32 vco_mode; + u32 real_clock; +}; + extern enum radeon_tv_std radeon_combios_get_tv_info(struct radeon_device *rdev); extern enum radeon_tv_std diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index d3aface..1424ccd 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -321,8 +321,10 @@ void radeon_bo_force_delete(struct radeon_device *rdev) int radeon_bo_init(struct radeon_device *rdev) { /* Add an MTRR for the VRAM */ - rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size, + if (!rdev->fastfb_working) { + rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size, MTRR_TYPE_WRCOMB, 1); + } DRM_INFO("Detected VRAM RAM=%lluM, BAR=%lluM\n", rdev->mc.mc_vram_size >> 20, (unsigned long long)rdev->mc.aper_size >> 20); @@ -339,14 +341,14 @@ void radeon_bo_fini(struct radeon_device *rdev) void radeon_bo_list_add_object(struct radeon_bo_list *lobj, struct list_head *head) { - if (lobj->wdomain) { + if (lobj->written) { list_add(&lobj->tv.head, head); } else { list_add_tail(&lobj->tv.head, head); } } -int radeon_bo_list_validate(struct list_head *head) +int radeon_bo_list_validate(struct list_head *head, int ring) { struct radeon_bo_list *lobj; struct radeon_bo *bo; @@ -360,15 +362,17 @@ int radeon_bo_list_validate(struct list_head *head) list_for_each_entry(lobj, head, tv.head) { bo = lobj->bo; if (!bo->pin_count) { - domain = lobj->wdomain ? lobj->wdomain : lobj->rdomain; + domain = lobj->domain; retry: radeon_ttm_placement_from_domain(bo, domain); + if (ring == R600_RING_TYPE_UVD_INDEX) + radeon_uvd_force_into_uvd_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); if (unlikely(r)) { - if (r != -ERESTARTSYS && domain == RADEON_GEM_DOMAIN_VRAM) { - domain |= RADEON_GEM_DOMAIN_GTT; + if (r != -ERESTARTSYS && domain != lobj->alt_domain) { + domain = lobj->alt_domain; goto retry; } return r; diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index 5fc86b0..e2cb80a 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -128,7 +128,7 @@ extern int radeon_bo_init(struct radeon_device *rdev); extern void radeon_bo_fini(struct radeon_device *rdev); extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj, struct list_head *head); -extern int radeon_bo_list_validate(struct list_head *head); +extern int radeon_bo_list_validate(struct list_head *head, int ring); extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo, struct vm_area_struct *vma); extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo, diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 338fd6a..788c64c 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -843,7 +843,11 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data) struct radeon_device *rdev = dev->dev_private; seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); - seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); + /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ + if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) + seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); + else + seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); if (rdev->asic->pm.get_memory_clock) seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 8d58e26..e17faa7 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -180,7 +180,8 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib, radeon_semaphore_free(rdev, &ib->semaphore, NULL); } /* if we can't remember our last VM flush then flush now! */ - if (ib->vm && !ib->vm->last_flush) { + /* XXX figure out why we have to flush for every IB */ + if (ib->vm /*&& !ib->vm->last_flush*/) { radeon_ring_vm_flush(rdev, ib->ring, ib->vm); } if (const_ib) { @@ -368,7 +369,7 @@ void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring) { u32 rptr; - if (rdev->wb.enabled) + if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX]) rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]); else rptr = RREG32(ring->rptr_reg); @@ -821,18 +822,20 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data) return 0; } -static int radeon_ring_type_gfx_index = RADEON_RING_TYPE_GFX_INDEX; -static int cayman_ring_type_cp1_index = CAYMAN_RING_TYPE_CP1_INDEX; -static int cayman_ring_type_cp2_index = CAYMAN_RING_TYPE_CP2_INDEX; -static int radeon_ring_type_dma1_index = R600_RING_TYPE_DMA_INDEX; -static int radeon_ring_type_dma2_index = CAYMAN_RING_TYPE_DMA1_INDEX; +static int radeon_gfx_index = RADEON_RING_TYPE_GFX_INDEX; +static int cayman_cp1_index = CAYMAN_RING_TYPE_CP1_INDEX; +static int cayman_cp2_index = CAYMAN_RING_TYPE_CP2_INDEX; +static int radeon_dma1_index = R600_RING_TYPE_DMA_INDEX; +static int radeon_dma2_index = CAYMAN_RING_TYPE_DMA1_INDEX; +static int r600_uvd_index = R600_RING_TYPE_UVD_INDEX; static struct drm_info_list radeon_debugfs_ring_info_list[] = { - {"radeon_ring_gfx", radeon_debugfs_ring_info, 0, &radeon_ring_type_gfx_index}, - {"radeon_ring_cp1", radeon_debugfs_ring_info, 0, &cayman_ring_type_cp1_index}, - {"radeon_ring_cp2", radeon_debugfs_ring_info, 0, &cayman_ring_type_cp2_index}, - {"radeon_ring_dma1", radeon_debugfs_ring_info, 0, &radeon_ring_type_dma1_index}, - {"radeon_ring_dma2", radeon_debugfs_ring_info, 0, &radeon_ring_type_dma2_index}, + {"radeon_ring_gfx", radeon_debugfs_ring_info, 0, &radeon_gfx_index}, + {"radeon_ring_cp1", radeon_debugfs_ring_info, 0, &cayman_cp1_index}, + {"radeon_ring_cp2", radeon_debugfs_ring_info, 0, &cayman_cp2_index}, + {"radeon_ring_dma1", radeon_debugfs_ring_info, 0, &radeon_dma1_index}, + {"radeon_ring_dma2", radeon_debugfs_ring_info, 0, &radeon_dma2_index}, + {"radeon_ring_uvd", radeon_debugfs_ring_info, 0, &r600_uvd_index}, }; static int radeon_debugfs_sa_info(struct seq_file *m, void *data) diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index fda09c9..bbed4af 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -252,6 +252,36 @@ void radeon_test_moves(struct radeon_device *rdev) radeon_do_test_moves(rdev, RADEON_TEST_COPY_BLIT); } +static int radeon_test_create_and_emit_fence(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_fence **fence) +{ + int r; + + if (ring->idx == R600_RING_TYPE_UVD_INDEX) { + r = radeon_uvd_get_create_msg(rdev, ring->idx, 1, NULL); + if (r) { + DRM_ERROR("Failed to get dummy create msg\n"); + return r; + } + + r = radeon_uvd_get_destroy_msg(rdev, ring->idx, 1, fence); + if (r) { + DRM_ERROR("Failed to get dummy destroy msg\n"); + return r; + } + } else { + r = radeon_ring_lock(rdev, ring, 64); + if (r) { + DRM_ERROR("Failed to lock ring A %d\n", ring->idx); + return r; + } + radeon_fence_emit(rdev, fence, ring->idx); + radeon_ring_unlock_commit(rdev, ring); + } + return 0; +} + void radeon_test_ring_sync(struct radeon_device *rdev, struct radeon_ring *ringA, struct radeon_ring *ringB) @@ -272,21 +302,24 @@ void radeon_test_ring_sync(struct radeon_device *rdev, goto out_cleanup; } radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); - r = radeon_fence_emit(rdev, &fence1, ringA->idx); - if (r) { - DRM_ERROR("Failed to emit fence 1\n"); - radeon_ring_unlock_undo(rdev, ringA); + radeon_ring_unlock_commit(rdev, ringA); + + r = radeon_test_create_and_emit_fence(rdev, ringA, &fence1); + if (r) goto out_cleanup; - } - radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); - r = radeon_fence_emit(rdev, &fence2, ringA->idx); + + r = radeon_ring_lock(rdev, ringA, 64); if (r) { - DRM_ERROR("Failed to emit fence 2\n"); - radeon_ring_unlock_undo(rdev, ringA); + DRM_ERROR("Failed to lock ring A %d\n", ringA->idx); goto out_cleanup; } + radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); radeon_ring_unlock_commit(rdev, ringA); + r = radeon_test_create_and_emit_fence(rdev, ringA, &fence2); + if (r) + goto out_cleanup; + mdelay(1000); if (radeon_fence_signaled(fence1)) { @@ -364,27 +397,22 @@ static void radeon_test_ring_sync2(struct radeon_device *rdev, goto out_cleanup; } radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); - r = radeon_fence_emit(rdev, &fenceA, ringA->idx); - if (r) { - DRM_ERROR("Failed to emit sync fence 1\n"); - radeon_ring_unlock_undo(rdev, ringA); - goto out_cleanup; - } radeon_ring_unlock_commit(rdev, ringA); + r = radeon_test_create_and_emit_fence(rdev, ringA, &fenceA); + if (r) + goto out_cleanup; + r = radeon_ring_lock(rdev, ringB, 64); if (r) { DRM_ERROR("Failed to lock ring B %d\n", ringB->idx); goto out_cleanup; } radeon_semaphore_emit_wait(rdev, ringB->idx, semaphore); - r = radeon_fence_emit(rdev, &fenceB, ringB->idx); - if (r) { - DRM_ERROR("Failed to create sync fence 2\n"); - radeon_ring_unlock_undo(rdev, ringB); - goto out_cleanup; - } radeon_ring_unlock_commit(rdev, ringB); + r = radeon_test_create_and_emit_fence(rdev, ringB, &fenceB); + if (r) + goto out_cleanup; mdelay(1000); @@ -393,7 +421,7 @@ static void radeon_test_ring_sync2(struct radeon_device *rdev, goto out_cleanup; } if (radeon_fence_signaled(fenceB)) { - DRM_ERROR("Fence A signaled without waiting for semaphore.\n"); + DRM_ERROR("Fence B signaled without waiting for semaphore.\n"); goto out_cleanup; } diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c new file mode 100644 index 0000000..30a9460 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -0,0 +1,666 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Christian König <deathsimple@vodafone.de> + */ + +#include <linux/firmware.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include <drm/drm.h> + +#include "radeon.h" +#include "r600d.h" + +/* Firmware Names */ +#define FIRMWARE_RV710 "radeon/RV710_uvd.bin" +#define FIRMWARE_CYPRESS "radeon/CYPRESS_uvd.bin" +#define FIRMWARE_SUMO "radeon/SUMO_uvd.bin" +#define FIRMWARE_TAHITI "radeon/TAHITI_uvd.bin" + +MODULE_FIRMWARE(FIRMWARE_RV710); +MODULE_FIRMWARE(FIRMWARE_CYPRESS); +MODULE_FIRMWARE(FIRMWARE_SUMO); +MODULE_FIRMWARE(FIRMWARE_TAHITI); + +int radeon_uvd_init(struct radeon_device *rdev) +{ + struct platform_device *pdev; + unsigned long bo_size; + const char *fw_name; + int i, r; + + pdev = platform_device_register_simple("radeon_uvd", 0, NULL, 0); + r = IS_ERR(pdev); + if (r) { + dev_err(rdev->dev, "radeon_uvd: Failed to register firmware\n"); + return -EINVAL; + } + + switch (rdev->family) { + case CHIP_RV710: + case CHIP_RV730: + case CHIP_RV740: + fw_name = FIRMWARE_RV710; + break; + + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + case CHIP_JUNIPER: + case CHIP_REDWOOD: + case CHIP_CEDAR: + fw_name = FIRMWARE_CYPRESS; + break; + + case CHIP_SUMO: + case CHIP_SUMO2: + case CHIP_PALM: + case CHIP_CAYMAN: + case CHIP_BARTS: + case CHIP_TURKS: + case CHIP_CAICOS: + fw_name = FIRMWARE_SUMO; + break; + + case CHIP_TAHITI: + case CHIP_VERDE: + case CHIP_PITCAIRN: + case CHIP_ARUBA: + fw_name = FIRMWARE_TAHITI; + break; + + default: + return -EINVAL; + } + + r = request_firmware(&rdev->uvd_fw, fw_name, &pdev->dev); + if (r) { + dev_err(rdev->dev, "radeon_uvd: Can't load firmware \"%s\"\n", + fw_name); + platform_device_unregister(pdev); + return r; + } + + platform_device_unregister(pdev); + + bo_size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 4) + + RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE; + r = radeon_bo_create(rdev, bo_size, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->uvd.vcpu_bo); + if (r) { + dev_err(rdev->dev, "(%d) failed to allocate UVD bo\n", r); + return r; + } + + r = radeon_uvd_resume(rdev); + if (r) + return r; + + memset(rdev->uvd.cpu_addr, 0, bo_size); + memcpy(rdev->uvd.cpu_addr, rdev->uvd_fw->data, rdev->uvd_fw->size); + + r = radeon_uvd_suspend(rdev); + if (r) + return r; + + for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { + atomic_set(&rdev->uvd.handles[i], 0); + rdev->uvd.filp[i] = NULL; + } + + return 0; +} + +void radeon_uvd_fini(struct radeon_device *rdev) +{ + radeon_uvd_suspend(rdev); + radeon_bo_unref(&rdev->uvd.vcpu_bo); +} + +int radeon_uvd_suspend(struct radeon_device *rdev) +{ + int r; + + if (rdev->uvd.vcpu_bo == NULL) + return 0; + + r = radeon_bo_reserve(rdev->uvd.vcpu_bo, false); + if (!r) { + radeon_bo_kunmap(rdev->uvd.vcpu_bo); + radeon_bo_unpin(rdev->uvd.vcpu_bo); + radeon_bo_unreserve(rdev->uvd.vcpu_bo); + } + return r; +} + +int radeon_uvd_resume(struct radeon_device *rdev) +{ + int r; + + if (rdev->uvd.vcpu_bo == NULL) + return -EINVAL; + + r = radeon_bo_reserve(rdev->uvd.vcpu_bo, false); + if (r) { + radeon_bo_unref(&rdev->uvd.vcpu_bo); + dev_err(rdev->dev, "(%d) failed to reserve UVD bo\n", r); + return r; + } + + r = radeon_bo_pin(rdev->uvd.vcpu_bo, RADEON_GEM_DOMAIN_VRAM, + &rdev->uvd.gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->uvd.vcpu_bo); + radeon_bo_unref(&rdev->uvd.vcpu_bo); + dev_err(rdev->dev, "(%d) UVD bo pin failed\n", r); + return r; + } + + r = radeon_bo_kmap(rdev->uvd.vcpu_bo, &rdev->uvd.cpu_addr); + if (r) { + dev_err(rdev->dev, "(%d) UVD map failed\n", r); + return r; + } + + radeon_bo_unreserve(rdev->uvd.vcpu_bo); + + radeon_set_uvd_clocks(rdev, 53300, 40000); + + return 0; +} + +void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo) +{ + rbo->placement.fpfn = 0 >> PAGE_SHIFT; + rbo->placement.lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT; +} + +void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp) +{ + int i, r; + for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { + if (rdev->uvd.filp[i] == filp) { + uint32_t handle = atomic_read(&rdev->uvd.handles[i]); + struct radeon_fence *fence; + + r = radeon_uvd_get_destroy_msg(rdev, + R600_RING_TYPE_UVD_INDEX, handle, &fence); + if (r) { + DRM_ERROR("Error destroying UVD (%d)!\n", r); + continue; + } + + radeon_fence_wait(fence, false); + radeon_fence_unref(&fence); + + rdev->uvd.filp[i] = NULL; + atomic_set(&rdev->uvd.handles[i], 0); + } + } +} + +static int radeon_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) +{ + unsigned stream_type = msg[4]; + unsigned width = msg[6]; + unsigned height = msg[7]; + unsigned dpb_size = msg[9]; + unsigned pitch = msg[28]; + + unsigned width_in_mb = width / 16; + unsigned height_in_mb = ALIGN(height / 16, 2); + + unsigned image_size, tmp, min_dpb_size; + + image_size = width * height; + image_size += image_size / 2; + image_size = ALIGN(image_size, 1024); + + switch (stream_type) { + case 0: /* H264 */ + + /* reference picture buffer */ + min_dpb_size = image_size * 17; + + /* macroblock context buffer */ + min_dpb_size += width_in_mb * height_in_mb * 17 * 192; + + /* IT surface buffer */ + min_dpb_size += width_in_mb * height_in_mb * 32; + break; + + case 1: /* VC1 */ + + /* reference picture buffer */ + min_dpb_size = image_size * 3; + + /* CONTEXT_BUFFER */ + min_dpb_size += width_in_mb * height_in_mb * 128; + + /* IT surface buffer */ + min_dpb_size += width_in_mb * 64; + + /* DB surface buffer */ + min_dpb_size += width_in_mb * 128; + + /* BP */ + tmp = max(width_in_mb, height_in_mb); + min_dpb_size += ALIGN(tmp * 7 * 16, 64); + break; + + case 3: /* MPEG2 */ + + /* reference picture buffer */ + min_dpb_size = image_size * 3; + break; + + case 4: /* MPEG4 */ + + /* reference picture buffer */ + min_dpb_size = image_size * 3; + + /* CM */ + min_dpb_size += width_in_mb * height_in_mb * 64; + + /* IT surface buffer */ + min_dpb_size += ALIGN(width_in_mb * height_in_mb * 32, 64); + break; + + default: + DRM_ERROR("UVD codec not handled %d!\n", stream_type); + return -EINVAL; + } + + if (width > pitch) { + DRM_ERROR("Invalid UVD decoding target pitch!\n"); + return -EINVAL; + } + + if (dpb_size < min_dpb_size) { + DRM_ERROR("Invalid dpb_size in UVD message (%d / %d)!\n", + dpb_size, min_dpb_size); + return -EINVAL; + } + + buf_sizes[0x1] = dpb_size; + buf_sizes[0x2] = image_size; + return 0; +} + +static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, + unsigned offset, unsigned buf_sizes[]) +{ + int32_t *msg, msg_type, handle; + void *ptr; + + int i, r; + + if (offset & 0x3F) { + DRM_ERROR("UVD messages must be 64 byte aligned!\n"); + return -EINVAL; + } + + r = radeon_bo_kmap(bo, &ptr); + if (r) + return r; + + msg = ptr + offset; + + msg_type = msg[1]; + handle = msg[2]; + + if (handle == 0) { + DRM_ERROR("Invalid UVD handle!\n"); + return -EINVAL; + } + + if (msg_type == 1) { + /* it's a decode msg, calc buffer sizes */ + r = radeon_uvd_cs_msg_decode(msg, buf_sizes); + radeon_bo_kunmap(bo); + if (r) + return r; + + } else if (msg_type == 2) { + /* it's a destroy msg, free the handle */ + for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) + atomic_cmpxchg(&p->rdev->uvd.handles[i], handle, 0); + radeon_bo_kunmap(bo); + return 0; + } else { + /* it's a create msg, no special handling needed */ + radeon_bo_kunmap(bo); + } + + /* create or decode, validate the handle */ + for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { + if (atomic_read(&p->rdev->uvd.handles[i]) == handle) + return 0; + } + + /* handle not found try to alloc a new one */ + for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { + if (!atomic_cmpxchg(&p->rdev->uvd.handles[i], 0, handle)) { + p->rdev->uvd.filp[i] = p->filp; + return 0; + } + } + + DRM_ERROR("No more free UVD handles!\n"); + return -EINVAL; +} + +static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p, + int data0, int data1, + unsigned buf_sizes[]) +{ + struct radeon_cs_chunk *relocs_chunk; + struct radeon_cs_reloc *reloc; + unsigned idx, cmd, offset; + uint64_t start, end; + int r; + + relocs_chunk = &p->chunks[p->chunk_relocs_idx]; + offset = radeon_get_ib_value(p, data0); + idx = radeon_get_ib_value(p, data1); + if (idx >= relocs_chunk->length_dw) { + DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", + idx, relocs_chunk->length_dw); + return -EINVAL; + } + + reloc = p->relocs_ptr[(idx / 4)]; + start = reloc->lobj.gpu_offset; + end = start + radeon_bo_size(reloc->robj); + start += offset; + + p->ib.ptr[data0] = start & 0xFFFFFFFF; + p->ib.ptr[data1] = start >> 32; + + cmd = radeon_get_ib_value(p, p->idx) >> 1; + + if (cmd < 0x4) { + if ((end - start) < buf_sizes[cmd]) { + DRM_ERROR("buffer to small (%d / %d)!\n", + (unsigned)(end - start), buf_sizes[cmd]); + return -EINVAL; + } + + } else if (cmd != 0x100) { + DRM_ERROR("invalid UVD command %X!\n", cmd); + return -EINVAL; + } + + if (cmd == 0) { + if (end & 0xFFFFFFFFF0000000) { + DRM_ERROR("msg buffer %LX-%LX out of 256MB segment!\n", + start, end); + return -EINVAL; + } + + r = radeon_uvd_cs_msg(p, reloc->robj, offset, buf_sizes); + if (r) + return r; + } + + if ((start & 0xFFFFFFFFF0000000) != (end & 0xFFFFFFFFF0000000)) { + DRM_ERROR("reloc %LX-%LX crossing 256MB boundary!\n", + start, end); + return -EINVAL; + } + + return 0; +} + +static int radeon_uvd_cs_reg(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + int *data0, int *data1, + unsigned buf_sizes[]) +{ + int i, r; + + p->idx++; + for (i = 0; i <= pkt->count; ++i) { + switch (pkt->reg + i*4) { + case UVD_GPCOM_VCPU_DATA0: + *data0 = p->idx; + break; + case UVD_GPCOM_VCPU_DATA1: + *data1 = p->idx; + break; + case UVD_GPCOM_VCPU_CMD: + r = radeon_uvd_cs_reloc(p, *data0, *data1, buf_sizes); + if (r) + return r; + break; + case UVD_ENGINE_CNTL: + break; + default: + DRM_ERROR("Invalid reg 0x%X!\n", + pkt->reg + i*4); + return -EINVAL; + } + p->idx++; + } + return 0; +} + +int radeon_uvd_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet pkt; + int r, data0 = 0, data1 = 0; + + /* minimum buffer sizes */ + unsigned buf_sizes[] = { + [0x00000000] = 2048, + [0x00000001] = 32 * 1024 * 1024, + [0x00000002] = 2048 * 1152 * 3, + [0x00000003] = 2048, + }; + + if (p->chunks[p->chunk_ib_idx].length_dw % 16) { + DRM_ERROR("UVD IB length (%d) not 16 dwords aligned!\n", + p->chunks[p->chunk_ib_idx].length_dw); + return -EINVAL; + } + + if (p->chunk_relocs_idx == -1) { + DRM_ERROR("No relocation chunk !\n"); + return -EINVAL; + } + + + do { + r = radeon_cs_packet_parse(p, &pkt, p->idx); + if (r) + return r; + switch (pkt.type) { + case RADEON_PACKET_TYPE0: + r = radeon_uvd_cs_reg(p, &pkt, &data0, + &data1, buf_sizes); + if (r) + return r; + break; + case RADEON_PACKET_TYPE2: + p->idx += pkt.count + 2; + break; + default: + DRM_ERROR("Unknown packet type %d !\n", pkt.type); + return -EINVAL; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); + return 0; +} + +static int radeon_uvd_send_msg(struct radeon_device *rdev, + int ring, struct radeon_bo *bo, + struct radeon_fence **fence) +{ + struct ttm_validate_buffer tv; + struct list_head head; + struct radeon_ib ib; + uint64_t addr; + int i, r; + + memset(&tv, 0, sizeof(tv)); + tv.bo = &bo->tbo; + + INIT_LIST_HEAD(&head); + list_add(&tv.head, &head); + + r = ttm_eu_reserve_buffers(&head); + if (r) + return r; + + radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_VRAM); + radeon_uvd_force_into_uvd_segment(bo); + + r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); + if (r) { + ttm_eu_backoff_reservation(&head); + return r; + } + + r = radeon_ib_get(rdev, ring, &ib, NULL, 16); + if (r) { + ttm_eu_backoff_reservation(&head); + return r; + } + + addr = radeon_bo_gpu_offset(bo); + ib.ptr[0] = PACKET0(UVD_GPCOM_VCPU_DATA0, 0); + ib.ptr[1] = addr; + ib.ptr[2] = PACKET0(UVD_GPCOM_VCPU_DATA1, 0); + ib.ptr[3] = addr >> 32; + ib.ptr[4] = PACKET0(UVD_GPCOM_VCPU_CMD, 0); + ib.ptr[5] = 0; + for (i = 6; i < 16; ++i) + ib.ptr[i] = PACKET2(0); + ib.length_dw = 16; + + r = radeon_ib_schedule(rdev, &ib, NULL); + if (r) { + ttm_eu_backoff_reservation(&head); + return r; + } + ttm_eu_fence_buffer_objects(&head, ib.fence); + + if (fence) + *fence = radeon_fence_ref(ib.fence); + + radeon_ib_free(rdev, &ib); + radeon_bo_unref(&bo); + return 0; +} + +/* multiple fence commands without any stream commands in between can + crash the vcpu so just try to emmit a dummy create/destroy msg to + avoid this */ +int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, + uint32_t handle, struct radeon_fence **fence) +{ + struct radeon_bo *bo; + uint32_t *msg; + int r, i; + + r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, &bo); + if (r) + return r; + + r = radeon_bo_reserve(bo, false); + if (r) { + radeon_bo_unref(&bo); + return r; + } + + r = radeon_bo_kmap(bo, (void **)&msg); + if (r) { + radeon_bo_unreserve(bo); + radeon_bo_unref(&bo); + return r; + } + + /* stitch together an UVD create msg */ + msg[0] = 0x00000de4; + msg[1] = 0x00000000; + msg[2] = handle; + msg[3] = 0x00000000; + msg[4] = 0x00000000; + msg[5] = 0x00000000; + msg[6] = 0x00000000; + msg[7] = 0x00000780; + msg[8] = 0x00000440; + msg[9] = 0x00000000; + msg[10] = 0x01b37000; + for (i = 11; i < 1024; ++i) + msg[i] = 0x0; + + radeon_bo_kunmap(bo); + radeon_bo_unreserve(bo); + + return radeon_uvd_send_msg(rdev, ring, bo, fence); +} + +int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, + uint32_t handle, struct radeon_fence **fence) +{ + struct radeon_bo *bo; + uint32_t *msg; + int r, i; + + r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, &bo); + if (r) + return r; + + r = radeon_bo_reserve(bo, false); + if (r) { + radeon_bo_unref(&bo); + return r; + } + + r = radeon_bo_kmap(bo, (void **)&msg); + if (r) { + radeon_bo_unreserve(bo); + radeon_bo_unref(&bo); + return r; + } + + /* stitch together an UVD destroy msg */ + msg[0] = 0x00000de4; + msg[1] = 0x00000002; + msg[2] = handle; + msg[3] = 0x00000000; + for (i = 4; i < 1024; ++i) + msg[i] = 0x0; + + radeon_bo_kunmap(bo); + radeon_bo_unreserve(bo); + + return radeon_uvd_send_msg(rdev, ring, bo, fence); +} diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 5706d2a..ab4c86c 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -148,6 +148,8 @@ void rs690_pm_info(struct radeon_device *rdev) static void rs690_mc_init(struct radeon_device *rdev) { u64 base; + uint32_t h_addr, l_addr; + unsigned long long k8_addr; rs400_gart_adjust_size(rdev); rdev->mc.vram_is_ddr = true; @@ -160,6 +162,27 @@ static void rs690_mc_init(struct radeon_device *rdev) base = RREG32_MC(R_000100_MCCFG_FB_LOCATION); base = G_000100_MC_FB_START(base) << 16; rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev); + + /* Use K8 direct mapping for fast fb access. */ + rdev->fastfb_working = false; + h_addr = G_00005F_K8_ADDR_EXT(RREG32_MC(R_00005F_MC_MISC_UMA_CNTL)); + l_addr = RREG32_MC(R_00001E_K8_FB_LOCATION); + k8_addr = ((unsigned long long)h_addr) << 32 | l_addr; +#if defined(CONFIG_X86_32) && !defined(CONFIG_X86_PAE) + if (k8_addr + rdev->mc.visible_vram_size < 0x100000000ULL) +#endif + { + /* FastFB shall be used with UMA memory. Here it is simply disabled when sideport + * memory is present. + */ + if (rdev->mc.igp_sideport_enabled == false && radeon_fastfb == 1) { + DRM_INFO("Direct mapping: aper base at 0x%llx, replaced by direct mapping base 0x%llx.\n", + (unsigned long long)rdev->mc.aper_base, k8_addr); + rdev->mc.aper_base = (resource_size_t)k8_addr; + rdev->fastfb_working = true; + } + } + rs690_pm_info(rdev); radeon_vram_location(rdev, &rdev->mc, base); rdev->mc.gtt_base_align = rdev->mc.gtt_size - 1; diff --git a/drivers/gpu/drm/radeon/rs690d.h b/drivers/gpu/drm/radeon/rs690d.h index 36e6398..8af3ccf 100644 --- a/drivers/gpu/drm/radeon/rs690d.h +++ b/drivers/gpu/drm/radeon/rs690d.h @@ -29,6 +29,9 @@ #define __RS690D_H__ /* Registers */ +#define R_00001E_K8_FB_LOCATION 0x00001E +#define R_00005F_MC_MISC_UMA_CNTL 0x00005F +#define G_00005F_K8_ADDR_EXT(x) (((x) >> 0) & 0xFF) #define R_000078_MC_INDEX 0x000078 #define S_000078_MC_IND_ADDR(x) (((x) & 0x1FF) << 0) #define G_000078_MC_IND_ADDR(x) (((x) >> 0) & 0x1FF) diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 435ed35..5e1ba16 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -348,7 +348,7 @@ void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save) /* update crtc base addresses */ for (i = 0; i < rdev->num_crtc; i++) { if (rdev->family >= CHIP_RV770) { - if (i == 1) { + if (i == 0) { WREG32(R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(rdev->mc.vram_start)); WREG32(R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index d63fe1d..777f537 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -42,6 +42,162 @@ static void rv770_gpu_init(struct radeon_device *rdev); void rv770_fini(struct radeon_device *rdev); static void rv770_pcie_gen2_enable(struct radeon_device *rdev); +int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); + +static int rv770_uvd_calc_post_div(unsigned target_freq, + unsigned vco_freq, + unsigned *div) +{ + /* Fclk = Fvco / PDIV */ + *div = vco_freq / target_freq; + + /* we alway need a frequency less than or equal the target */ + if ((vco_freq / *div) > target_freq) + *div += 1; + + /* out of range ? */ + if (*div > 30) + return -1; /* forget it */ + + *div -= 1; + return vco_freq / (*div + 1); +} + +static int rv770_uvd_send_upll_ctlreq(struct radeon_device *rdev) +{ + unsigned i; + + /* assert UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); + + /* wait for CTLACK and CTLACK2 to get asserted */ + for (i = 0; i < 100; ++i) { + uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; + if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; + + /* deassert UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); + + return 0; +} + +int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) +{ + /* start off with something large */ + int optimal_diff_score = 0x7FFFFFF; + unsigned optimal_fb_div = 0, optimal_vclk_div = 0; + unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; + unsigned vco_freq, vco_min = 50000, vco_max = 160000; + unsigned ref_freq = rdev->clock.spll.reference_freq; + int r; + + /* RV740 uses evergreen uvd clk programming */ + if (rdev->family == CHIP_RV740) + return evergreen_set_uvd_clocks(rdev, vclk, dclk); + + /* loop through vco from low to high */ + vco_min = max(max(vco_min, vclk), dclk); + for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 500) { + uint64_t fb_div = (uint64_t)vco_freq * 43663; + int calc_clk, diff_score, diff_vclk, diff_dclk; + unsigned vclk_div, dclk_div; + + do_div(fb_div, ref_freq); + fb_div |= 1; + + /* fb div out of range ? */ + if (fb_div > 0x03FFFFFF) + break; /* it can oly get worse */ + + /* calc vclk with current vco freq. */ + calc_clk = rv770_uvd_calc_post_div(vclk, vco_freq, &vclk_div); + if (calc_clk == -1) + break; /* vco is too big, it has to stop. */ + diff_vclk = vclk - calc_clk; + + /* calc dclk with current vco freq. */ + calc_clk = rv770_uvd_calc_post_div(dclk, vco_freq, &dclk_div); + if (calc_clk == -1) + break; /* vco is too big, it has to stop. */ + diff_dclk = dclk - calc_clk; + + /* determine if this vco setting is better than current optimal settings */ + diff_score = abs(diff_vclk) + abs(diff_dclk); + if (diff_score < optimal_diff_score) { + optimal_fb_div = fb_div; + optimal_vclk_div = vclk_div; + optimal_dclk_div = dclk_div; + optimal_vco_freq = vco_freq; + optimal_diff_score = diff_score; + if (optimal_diff_score == 0) + break; /* it can't get better than this */ + } + } + + /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + /* set UPLL_FB_DIV to 0x50000 */ + WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK); + + /* deassert UPLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + /* assert BYPASS EN and FB_DIV[0] <- ??? why? */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); + WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(1), ~UPLL_FB_DIV(1)); + + r = rv770_uvd_send_upll_ctlreq(rdev); + if (r) + return r; + + /* assert PLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK); + + /* set the required FB_DIV, REF_DIV, Post divder values */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REF_DIV(1), ~UPLL_REF_DIV_MASK); + WREG32_P(CG_UPLL_FUNC_CNTL_2, + UPLL_SW_HILEN(optimal_vclk_div >> 1) | + UPLL_SW_LOLEN((optimal_vclk_div >> 1) + (optimal_vclk_div & 1)) | + UPLL_SW_HILEN2(optimal_dclk_div >> 1) | + UPLL_SW_LOLEN2((optimal_dclk_div >> 1) + (optimal_dclk_div & 1)), + ~UPLL_SW_MASK); + + WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), + ~UPLL_FB_DIV_MASK); + + /* give the PLL some time to settle */ + mdelay(15); + + /* deassert PLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + mdelay(15); + + /* deassert BYPASS EN and FB_DIV[0] <- ??? why? */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); + WREG32_P(CG_UPLL_FUNC_CNTL_3, 0, ~UPLL_FB_DIV(1)); + + r = rv770_uvd_send_upll_ctlreq(rdev); + if (r) + return r; + + /* switch VCLK and DCLK selection */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + mdelay(100); + + return 0; +} #define PCIE_BUS_CLK 10000 #define TCLK (PCIE_BUS_CLK / 10) @@ -68,6 +224,105 @@ u32 rv770_get_xclk(struct radeon_device *rdev) return reference_clock; } +int rv770_uvd_resume(struct radeon_device *rdev) +{ + uint64_t addr; + uint32_t chip_id, size; + int r; + + r = radeon_uvd_resume(rdev); + if (r) + return r; + + /* programm the VCPU memory controller bits 0-27 */ + addr = rdev->uvd.gpu_addr >> 3; + size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 4) >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET0, addr); + WREG32(UVD_VCPU_CACHE_SIZE0, size); + + addr += size; + size = RADEON_UVD_STACK_SIZE >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET1, addr); + WREG32(UVD_VCPU_CACHE_SIZE1, size); + + addr += size; + size = RADEON_UVD_HEAP_SIZE >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET2, addr); + WREG32(UVD_VCPU_CACHE_SIZE2, size); + + /* bits 28-31 */ + addr = (rdev->uvd.gpu_addr >> 28) & 0xF; + WREG32(UVD_LMI_ADDR_EXT, (addr << 12) | (addr << 0)); + + /* bits 32-39 */ + addr = (rdev->uvd.gpu_addr >> 32) & 0xFF; + WREG32(UVD_LMI_EXT40_ADDR, addr | (0x9 << 16) | (0x1 << 31)); + + /* tell firmware which hardware it is running on */ + switch (rdev->family) { + default: + return -EINVAL; + case CHIP_RV710: + chip_id = 0x01000005; + break; + case CHIP_RV730: + chip_id = 0x01000006; + break; + case CHIP_RV740: + chip_id = 0x01000007; + break; + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + chip_id = 0x01000008; + break; + case CHIP_JUNIPER: + chip_id = 0x01000009; + break; + case CHIP_REDWOOD: + chip_id = 0x0100000a; + break; + case CHIP_CEDAR: + chip_id = 0x0100000b; + break; + case CHIP_SUMO: + chip_id = 0x0100000c; + break; + case CHIP_SUMO2: + chip_id = 0x0100000d; + break; + case CHIP_PALM: + chip_id = 0x0100000e; + break; + case CHIP_CAYMAN: + chip_id = 0x0100000f; + break; + case CHIP_BARTS: + chip_id = 0x01000010; + break; + case CHIP_TURKS: + chip_id = 0x01000011; + break; + case CHIP_CAICOS: + chip_id = 0x01000012; + break; + case CHIP_TAHITI: + chip_id = 0x01000014; + break; + case CHIP_VERDE: + chip_id = 0x01000015; + break; + case CHIP_PITCAIRN: + chip_id = 0x01000016; + break; + case CHIP_ARUBA: + chip_id = 0x01000017; + break; + } + WREG32(UVD_VCPU_CHIP_ID, chip_id); + + return 0; +} + u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) { struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; @@ -611,6 +866,11 @@ static void rv770_gpu_init(struct radeon_device *rdev) WREG32(HDP_TILING_CONFIG, (gb_tiling_config & 0xffff)); WREG32(DMA_TILING_CONFIG, (gb_tiling_config & 0xffff)); WREG32(DMA_TILING_CONFIG2, (gb_tiling_config & 0xffff)); + if (rdev->family == CHIP_RV730) { + WREG32(UVD_UDEC_DB_TILING_CONFIG, (gb_tiling_config & 0xffff)); + WREG32(UVD_UDEC_DBW_TILING_CONFIG, (gb_tiling_config & 0xffff)); + WREG32(UVD_UDEC_TILING_CONFIG, (gb_tiling_config & 0xffff)); + } WREG32(CGTS_SYS_TCC_DISABLE, 0); WREG32(CGTS_TCC_DISABLE, 0); @@ -840,7 +1100,7 @@ void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) } if (rdev->flags & RADEON_IS_AGP) { size_bf = mc->gtt_start; - size_af = 0xFFFFFFFF - mc->gtt_end; + size_af = mc->mc_mask - mc->gtt_end; if (size_bf > size_af) { if (mc->mc_vram_size > size_bf) { dev_warn(rdev->dev, "limiting VRAM\n"); @@ -1040,6 +1300,17 @@ static int rv770_startup(struct radeon_device *rdev) return r; } + r = rv770_uvd_resume(rdev); + if (!r) { + r = radeon_fence_driver_start_ring(rdev, + R600_RING_TYPE_UVD_INDEX); + if (r) + dev_err(rdev->dev, "UVD fences init error (%d).\n", r); + } + + if (r) + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0; + /* Enable IRQ */ r = r600_irq_init(rdev); if (r) { @@ -1074,6 +1345,19 @@ static int rv770_startup(struct radeon_device *rdev) if (r) return r; + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + if (ring->ring_size) { + r = radeon_ring_init(rdev, ring, ring->ring_size, + R600_WB_UVD_RPTR_OFFSET, + UVD_RBC_RB_RPTR, UVD_RBC_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (!r) + r = r600_uvd_init(rdev); + + if (r) + DRM_ERROR("radeon: failed initializing UVD (%d).\n", r); + } + r = radeon_ib_pool_init(rdev); if (r) { dev_err(rdev->dev, "IB initialization failed (%d).\n", r); @@ -1115,6 +1399,7 @@ int rv770_resume(struct radeon_device *rdev) int rv770_suspend(struct radeon_device *rdev) { r600_audio_fini(rdev); + radeon_uvd_suspend(rdev); r700_cp_stop(rdev); r600_dma_stop(rdev); r600_irq_suspend(rdev); @@ -1190,6 +1475,13 @@ int rv770_init(struct radeon_device *rdev) rdev->ring[R600_RING_TYPE_DMA_INDEX].ring_obj = NULL; r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX], 64 * 1024); + r = radeon_uvd_init(rdev); + if (!r) { + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX], + 4096); + } + rdev->ih.ring_obj = NULL; r600_ih_ring_init(rdev, 64 * 1024); @@ -1224,6 +1516,7 @@ void rv770_fini(struct radeon_device *rdev) radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); rv770_pcie_gart_fini(rdev); + radeon_uvd_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); @@ -1264,23 +1557,23 @@ static void rv770_pcie_gen2_enable(struct radeon_device *rdev) DRM_INFO("enabling PCIE gen 2 link speeds, disable with radeon.pcie_gen2=0\n"); /* advertise upconfig capability */ - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); link_width_cntl &= ~LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); if (link_width_cntl & LC_RENEGOTIATION_SUPPORT) { lanes = (link_width_cntl & LC_LINK_WIDTH_RD_MASK) >> LC_LINK_WIDTH_RD_SHIFT; link_width_cntl &= ~(LC_LINK_WIDTH_MASK | LC_RECONFIG_ARC_MISSING_ESCAPE); link_width_cntl |= lanes | LC_RECONFIG_NOW | LC_RENEGOTIATE_EN | LC_UPCONFIGURE_SUPPORT; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } else { link_width_cntl |= LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); if ((speed_cntl & LC_OTHER_SIDE_EVER_SENT_GEN2) && (speed_cntl & LC_OTHER_SIDE_SUPPORTS_GEN2)) { @@ -1293,29 +1586,29 @@ static void rv770_pcie_gen2_enable(struct radeon_device *rdev) WREG16(0x4088, link_cntl2); WREG32(MM_CFGREGS_CNTL, 0); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl &= ~LC_TARGET_LINK_SPEED_OVERRIDE_EN; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl |= LC_CLR_FAILED_SPD_CHANGE_CNT; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); - speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); speed_cntl |= LC_GEN2_EN_STRAP; - WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl); } else { - link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ if (1) link_width_cntl |= LC_UPCONFIGURE_DIS; else link_width_cntl &= ~LC_UPCONFIGURE_DIS; - WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } } diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index c55f950..6a52b20 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -38,6 +38,30 @@ #define R7XX_MAX_PIPES 8 #define R7XX_MAX_PIPES_MASK 0xff +/* discrete uvd clocks */ +#define CG_UPLL_FUNC_CNTL 0x718 +# define UPLL_RESET_MASK 0x00000001 +# define UPLL_SLEEP_MASK 0x00000002 +# define UPLL_BYPASS_EN_MASK 0x00000004 +# define UPLL_CTLREQ_MASK 0x00000008 +# define UPLL_REF_DIV(x) ((x) << 16) +# define UPLL_REF_DIV_MASK 0x001F0000 +# define UPLL_CTLACK_MASK 0x40000000 +# define UPLL_CTLACK2_MASK 0x80000000 +#define CG_UPLL_FUNC_CNTL_2 0x71c +# define UPLL_SW_HILEN(x) ((x) << 0) +# define UPLL_SW_LOLEN(x) ((x) << 4) +# define UPLL_SW_HILEN2(x) ((x) << 8) +# define UPLL_SW_LOLEN2(x) ((x) << 12) +# define UPLL_SW_MASK 0x0000FFFF +# define VCLK_SRC_SEL(x) ((x) << 20) +# define VCLK_SRC_SEL_MASK 0x01F00000 +# define DCLK_SRC_SEL(x) ((x) << 25) +# define DCLK_SRC_SEL_MASK 0x3E000000 +#define CG_UPLL_FUNC_CNTL_3 0x720 +# define UPLL_FB_DIV(x) ((x) << 0) +# define UPLL_FB_DIV_MASK 0x01FFFFFF + /* Registers */ #define CB_COLOR0_BASE 0x28040 #define CB_COLOR1_BASE 0x28044 @@ -112,6 +136,11 @@ #define DMA_TILING_CONFIG 0x3ec8 #define DMA_TILING_CONFIG2 0xd0b8 +/* RV730 only */ +#define UVD_UDEC_TILING_CONFIG 0xef40 +#define UVD_UDEC_DB_TILING_CONFIG 0xef44 +#define UVD_UDEC_DBW_TILING_CONFIG 0xef48 + #define GC_USER_SHADER_PIPE_CONFIG 0x8954 #define INACTIVE_QD_PIPES(x) ((x) << 8) #define INACTIVE_QD_PIPES_MASK 0x0000FF00 @@ -671,4 +700,18 @@ # define TARGET_LINK_SPEED_MASK (0xf << 0) # define SELECTABLE_DEEMPHASIS (1 << 6) +/* UVD */ +#define UVD_LMI_EXT40_ADDR 0xf498 +#define UVD_VCPU_CHIP_ID 0xf4d4 +#define UVD_VCPU_CACHE_OFFSET0 0xf4d8 +#define UVD_VCPU_CACHE_SIZE0 0xf4dc +#define UVD_VCPU_CACHE_OFFSET1 0xf4e0 +#define UVD_VCPU_CACHE_SIZE1 0xf4e4 +#define UVD_VCPU_CACHE_OFFSET2 0xf4e8 +#define UVD_VCPU_CACHE_SIZE2 0xf4ec +#define UVD_LMI_ADDR_EXT 0xf594 + +#define UVD_RBC_RB_RPTR 0xf690 +#define UVD_RBC_RB_WPTR 0xf694 + #endif diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 80979ed..aa2c555 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -1211,6 +1211,7 @@ static void si_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.si.tile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); } } else if ((rdev->family == CHIP_VERDE) || @@ -1451,6 +1452,7 @@ static void si_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.si.tile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); } } else @@ -1765,9 +1767,13 @@ static void si_gpu_init(struct radeon_device *rdev) WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CALC, gb_addr_config); WREG32(HDP_ADDR_CONFIG, gb_addr_config); WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config); WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config); + WREG32(UVD_UDEC_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DB_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DBW_ADDR_CONFIG, gb_addr_config); si_tiling_mode_table_init(rdev); @@ -2284,6 +2290,12 @@ static u32 si_gpu_check_soft_reset(struct radeon_device *rdev) if (tmp & L2_BUSY) reset_mask |= RADEON_RESET_VMC; + /* Skip MC reset as it's mostly likely not hung, just busy */ + if (reset_mask & RADEON_RESET_MC) { + DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask); + reset_mask &= ~RADEON_RESET_MC; + } + return reset_mask; } @@ -2532,46 +2544,6 @@ static void si_mc_program(struct radeon_device *rdev) rv515_vga_render_disable(rdev); } -/* SI MC address space is 40 bits */ -static void si_vram_location(struct radeon_device *rdev, - struct radeon_mc *mc, u64 base) -{ - mc->vram_start = base; - if (mc->mc_vram_size > (0xFFFFFFFFFFULL - base + 1)) { - dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n"); - mc->real_vram_size = mc->aper_size; - mc->mc_vram_size = mc->aper_size; - } - mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; - dev_info(rdev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n", - mc->mc_vram_size >> 20, mc->vram_start, - mc->vram_end, mc->real_vram_size >> 20); -} - -static void si_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) -{ - u64 size_af, size_bf; - - size_af = ((0xFFFFFFFFFFULL - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; - size_bf = mc->vram_start & ~mc->gtt_base_align; - if (size_bf > size_af) { - if (mc->gtt_size > size_bf) { - dev_warn(rdev->dev, "limiting GTT\n"); - mc->gtt_size = size_bf; - } - mc->gtt_start = (mc->vram_start & ~mc->gtt_base_align) - mc->gtt_size; - } else { - if (mc->gtt_size > size_af) { - dev_warn(rdev->dev, "limiting GTT\n"); - mc->gtt_size = size_af; - } - mc->gtt_start = (mc->vram_end + 1 + mc->gtt_base_align) & ~mc->gtt_base_align; - } - mc->gtt_end = mc->gtt_start + mc->gtt_size - 1; - dev_info(rdev->dev, "GTT: %lluM 0x%016llX - 0x%016llX\n", - mc->gtt_size >> 20, mc->gtt_start, mc->gtt_end); -} - static void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) { @@ -2581,9 +2553,9 @@ static void si_vram_gtt_location(struct radeon_device *rdev, mc->real_vram_size = 0xFFC0000000ULL; mc->mc_vram_size = 0xFFC0000000ULL; } - si_vram_location(rdev, &rdev->mc, 0); + radeon_vram_location(rdev, &rdev->mc, 0); rdev->mc.gtt_base_align = 0; - si_gtt_location(rdev, mc); + radeon_gtt_location(rdev, mc); } static int si_mc_init(struct radeon_device *rdev) @@ -4316,14 +4288,6 @@ static int si_startup(struct radeon_device *rdev) return r; si_gpu_init(rdev); -#if 0 - r = evergreen_blit_init(rdev); - if (r) { - r600_blit_fini(rdev); - rdev->asic->copy = NULL; - dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); - } -#endif /* allocate rlc buffers */ r = si_rlc_init(rdev); if (r) { @@ -4366,6 +4330,16 @@ static int si_startup(struct radeon_device *rdev) return r; } + r = rv770_uvd_resume(rdev); + if (!r) { + r = radeon_fence_driver_start_ring(rdev, + R600_RING_TYPE_UVD_INDEX); + if (r) + dev_err(rdev->dev, "UVD fences init error (%d).\n", r); + } + if (r) + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0; + /* Enable IRQ */ r = si_irq_init(rdev); if (r) { @@ -4423,6 +4397,18 @@ static int si_startup(struct radeon_device *rdev) if (r) return r; + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + if (ring->ring_size) { + r = radeon_ring_init(rdev, ring, ring->ring_size, + R600_WB_UVD_RPTR_OFFSET, + UVD_RBC_RB_RPTR, UVD_RBC_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (!r) + r = r600_uvd_init(rdev); + if (r) + DRM_ERROR("radeon: failed initializing UVD (%d).\n", r); + } + r = radeon_ib_pool_init(rdev); if (r) { dev_err(rdev->dev, "IB initialization failed (%d).\n", r); @@ -4463,8 +4449,11 @@ int si_resume(struct radeon_device *rdev) int si_suspend(struct radeon_device *rdev) { + radeon_vm_manager_fini(rdev); si_cp_enable(rdev, false); cayman_dma_stop(rdev); + r600_uvd_rbc_stop(rdev); + radeon_uvd_suspend(rdev); si_irq_suspend(rdev); radeon_wb_disable(rdev); si_pcie_gart_disable(rdev); @@ -4550,6 +4539,13 @@ int si_init(struct radeon_device *rdev) ring->ring_obj = NULL; r600_ring_init(rdev, ring, 64 * 1024); + r = radeon_uvd_init(rdev); + if (!r) { + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 4096); + } + rdev->ih.ring_obj = NULL; r600_ih_ring_init(rdev, 64 * 1024); @@ -4587,9 +4583,6 @@ int si_init(struct radeon_device *rdev) void si_fini(struct radeon_device *rdev) { -#if 0 - r600_blit_fini(rdev); -#endif si_cp_fini(rdev); cayman_dma_fini(rdev); si_irq_fini(rdev); @@ -4598,6 +4591,7 @@ void si_fini(struct radeon_device *rdev) radeon_vm_manager_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); + radeon_uvd_fini(rdev); si_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); @@ -4627,3 +4621,170 @@ uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev) mutex_unlock(&rdev->gpu_clock_mutex); return clock; } + +static int si_uvd_calc_post_div(unsigned target_freq, + unsigned vco_freq, + unsigned *div) +{ + /* target larger than vco frequency ? */ + if (vco_freq < target_freq) + return -1; /* forget it */ + + /* Fclk = Fvco / PDIV */ + *div = vco_freq / target_freq; + + /* we alway need a frequency less than or equal the target */ + if ((vco_freq / *div) > target_freq) + *div += 1; + + /* dividers above 5 must be even */ + if (*div > 5 && *div % 2) + *div += 1; + + /* out of range ? */ + if (*div >= 128) + return -1; /* forget it */ + + return vco_freq / *div; +} + +static int si_uvd_send_upll_ctlreq(struct radeon_device *rdev) +{ + unsigned i; + + /* assert UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); + + /* wait for CTLACK and CTLACK2 to get asserted */ + for (i = 0; i < 100; ++i) { + uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; + if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; + + /* deassert UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); + + return 0; +} + +int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) +{ + /* start off with something large */ + int optimal_diff_score = 0x7FFFFFF; + unsigned optimal_fb_div = 0, optimal_vclk_div = 0; + unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; + unsigned vco_freq; + int r; + + /* loop through vco from low to high */ + for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { + unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; + int calc_clk, diff_score, diff_vclk, diff_dclk; + unsigned vclk_div, dclk_div; + + /* fb div out of range ? */ + if (fb_div > 0x03FFFFFF) + break; /* it can oly get worse */ + + /* calc vclk with current vco freq. */ + calc_clk = si_uvd_calc_post_div(vclk, vco_freq, &vclk_div); + if (calc_clk == -1) + break; /* vco is too big, it has to stop. */ + diff_vclk = vclk - calc_clk; + + /* calc dclk with current vco freq. */ + calc_clk = si_uvd_calc_post_div(dclk, vco_freq, &dclk_div); + if (calc_clk == -1) + break; /* vco is too big, it has to stop. */ + diff_dclk = dclk - calc_clk; + + /* determine if this vco setting is better than current optimal settings */ + diff_score = abs(diff_vclk) + abs(diff_dclk); + if (diff_score < optimal_diff_score) { + optimal_fb_div = fb_div; + optimal_vclk_div = vclk_div; + optimal_dclk_div = dclk_div; + optimal_vco_freq = vco_freq; + optimal_diff_score = diff_score; + if (optimal_diff_score == 0) + break; /* it can't get better than this */ + } + } + + /* set RESET_ANTI_MUX to 0 */ + WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK); + + /* set VCO_MODE to 1 */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); + + /* toggle UPLL_SLEEP to 1 then back to 0 */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK); + + /* deassert UPLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + mdelay(1); + + /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + /* put PLL in bypass mode */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); + + r = si_uvd_send_upll_ctlreq(rdev); + if (r) + return r; + + /* assert UPLL_RESET again */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK); + + /* disable spread spectrum. */ + WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); + + /* set feedback divider */ + WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK); + + /* set ref divider to 0 */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); + + if (optimal_vco_freq < 187500) + WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); + else + WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); + + /* set PDIV_A and PDIV_B */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div), + ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); + + /* give the PLL some time to settle */ + mdelay(15); + + /* deassert PLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + mdelay(15); + + /* switch from bypass mode to normal mode */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); + + r = si_uvd_send_upll_ctlreq(rdev); + if (r) + return r; + + /* switch VCLK and DCLK selection */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + mdelay(100); + + return 0; +} diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 23fc08f..042b91d 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -29,6 +29,35 @@ #define TAHITI_GB_ADDR_CONFIG_GOLDEN 0x12011003 #define VERDE_GB_ADDR_CONFIG_GOLDEN 0x12010002 +/* discrete uvd clocks */ +#define CG_UPLL_FUNC_CNTL 0x634 +# define UPLL_RESET_MASK 0x00000001 +# define UPLL_SLEEP_MASK 0x00000002 +# define UPLL_BYPASS_EN_MASK 0x00000004 +# define UPLL_CTLREQ_MASK 0x00000008 +# define UPLL_VCO_MODE_MASK 0x00000600 +# define UPLL_REF_DIV_MASK 0x001F0000 +# define UPLL_CTLACK_MASK 0x40000000 +# define UPLL_CTLACK2_MASK 0x80000000 +#define CG_UPLL_FUNC_CNTL_2 0x638 +# define UPLL_PDIV_A(x) ((x) << 0) +# define UPLL_PDIV_A_MASK 0x0000007F +# define UPLL_PDIV_B(x) ((x) << 8) +# define UPLL_PDIV_B_MASK 0x00007F00 +# define VCLK_SRC_SEL(x) ((x) << 20) +# define VCLK_SRC_SEL_MASK 0x01F00000 +# define DCLK_SRC_SEL(x) ((x) << 25) +# define DCLK_SRC_SEL_MASK 0x3E000000 +#define CG_UPLL_FUNC_CNTL_3 0x63C +# define UPLL_FB_DIV(x) ((x) << 0) +# define UPLL_FB_DIV_MASK 0x01FFFFFF +#define CG_UPLL_FUNC_CNTL_4 0x644 +# define UPLL_SPARE_ISPARE9 0x00020000 +#define CG_UPLL_FUNC_CNTL_5 0x648 +# define RESET_ANTI_MUX_MASK 0x00000200 +#define CG_UPLL_SPREAD_SPECTRUM 0x650 +# define SSEN_MASK 0x00000001 + #define CG_MULT_THERMAL_STATUS 0x714 #define ASIC_MAX_TEMP(x) ((x) << 0) #define ASIC_MAX_TEMP_MASK 0x000001ff @@ -65,6 +94,8 @@ #define DMIF_ADDR_CONFIG 0xBD4 +#define DMIF_ADDR_CALC 0xC00 + #define SRBM_STATUS 0xE50 #define GRBM_RQ_PENDING (1 << 5) #define VMC_BUSY (1 << 8) @@ -798,6 +829,15 @@ # define THREAD_TRACE_FINISH (55 << 0) /* + * UVD + */ +#define UVD_UDEC_ADDR_CONFIG 0xEF4C +#define UVD_UDEC_DB_ADDR_CONFIG 0xEF50 +#define UVD_UDEC_DBW_ADDR_CONFIG 0xEF54 +#define UVD_RBC_RB_RPTR 0xF690 +#define UVD_RBC_RB_WPTR 0xF694 + +/* * PM4 */ #define PACKET0(reg, n) ((RADEON_PACKET_TYPE0 << 30) | \ diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 841065b..5a5325e 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -58,7 +58,6 @@ static int sis_driver_unload(struct drm_device *dev) { drm_sis_private_t *dev_priv = dev->dev_private; - idr_remove_all(&dev_priv->object_idr); idr_destroy(&dev_priv->object_idr); kfree(dev_priv); diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 2b2f78c..9a43d98 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -128,17 +128,10 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, if (retval) goto fail_alloc; -again: - if (idr_pre_get(&dev_priv->object_idr, GFP_KERNEL) == 0) { - retval = -ENOMEM; - goto fail_idr; - } - - retval = idr_get_new_above(&dev_priv->object_idr, item, 1, &user_key); - if (retval == -EAGAIN) - goto again; - if (retval) + retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); + if (retval < 0) goto fail_idr; + user_key = retval; list_add(&item->owner_list, &file_priv->obj_list); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig index c92955d..be1daf7 100644 --- a/drivers/gpu/drm/tegra/Kconfig +++ b/drivers/gpu/drm/tegra/Kconfig @@ -4,7 +4,6 @@ config DRM_TEGRA select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER - select DRM_HDMI select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index bf8095c..de94707 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -12,8 +12,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> - -#include <mach/clk.h> +#include <linux/clk/tegra.h> #include "drm.h" #include "dc.h" @@ -1127,11 +1126,9 @@ static int tegra_dc_probe(struct platform_device *pdev) return -ENXIO; } - dc->regs = devm_request_and_ioremap(&pdev->dev, regs); - if (!dc->regs) { - dev_err(&pdev->dev, "failed to remap registers\n"); - return -ENXIO; - } + dc->regs = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(dc->regs)) + return PTR_ERR(dc->regs); dc->irq = platform_get_irq(pdev, 0); if (dc->irq < 0) { diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 181a370..9d452df 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -11,7 +11,6 @@ #include <linux/of_address.h> #include <linux/of_platform.h> -#include <mach/clk.h> #include <linux/dma-mapping.h> #include <asm/dma-iommu.h> diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 0daee8e..bb747f6 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -15,8 +15,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> - -#include <mach/clk.h> +#include <linux/clk/tegra.h> #include <drm/drm_edid.h> @@ -1251,9 +1250,9 @@ static int tegra_hdmi_probe(struct platform_device *pdev) if (!regs) return -ENXIO; - hdmi->regs = devm_request_and_ioremap(&pdev->dev, regs); - if (!hdmi->regs) - return -EADDRNOTAVAIL; + hdmi->regs = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(hdmi->regs)) + return PTR_ERR(hdmi->regs); err = platform_get_irq(pdev, 0); if (err < 0) diff --git a/drivers/gpu/drm/tegra/host1x.c b/drivers/gpu/drm/tegra/host1x.c index 5d17b11..92e25a7 100644 --- a/drivers/gpu/drm/tegra/host1x.c +++ b/drivers/gpu/drm/tegra/host1x.c @@ -139,9 +139,9 @@ static int tegra_host1x_probe(struct platform_device *pdev) host1x->irq = err; - host1x->regs = devm_request_and_ioremap(&pdev->dev, regs); - if (!host1x->regs) { - err = -EADDRNOTAVAIL; + host1x->regs = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host1x->regs)) { + err = PTR_ERR(host1x->regs); goto err; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 44420fc..af89458 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -86,6 +86,7 @@ int ttm_mem_io_lock(struct ttm_mem_type_manager *man, bool interruptible) mutex_lock(&man->io_reserve_mutex); return 0; } +EXPORT_SYMBOL(ttm_mem_io_lock); void ttm_mem_io_unlock(struct ttm_mem_type_manager *man) { @@ -94,6 +95,7 @@ void ttm_mem_io_unlock(struct ttm_mem_type_manager *man) mutex_unlock(&man->io_reserve_mutex); } +EXPORT_SYMBOL(ttm_mem_io_unlock); static int ttm_mem_io_evict(struct ttm_mem_type_manager *man) { @@ -111,8 +113,9 @@ static int ttm_mem_io_evict(struct ttm_mem_type_manager *man) return 0; } -static int ttm_mem_io_reserve(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) + +int ttm_mem_io_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; int ret = 0; @@ -134,9 +137,10 @@ retry: } return ret; } +EXPORT_SYMBOL(ttm_mem_io_reserve); -static void ttm_mem_io_free(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) +void ttm_mem_io_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; @@ -149,6 +153,7 @@ static void ttm_mem_io_free(struct ttm_bo_device *bdev, bdev->driver->io_mem_free(bdev, mem); } +EXPORT_SYMBOL(ttm_mem_io_free); int ttm_mem_io_reserve_vm(struct ttm_buffer_object *bo) { @@ -429,7 +434,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, struct ttm_bo_device *bdev = bo->bdev; struct ttm_bo_driver *driver = bdev->driver; - fbo = kzalloc(sizeof(*fbo), GFP_KERNEL); + fbo = kmalloc(sizeof(*fbo), GFP_KERNEL); if (!fbo) return -ENOMEM; @@ -448,7 +453,12 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, fbo->vm_node = NULL; atomic_set(&fbo->cpu_writers, 0); - fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); + spin_lock(&bdev->fence_lock); + if (bo->sync_obj) + fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); + else + fbo->sync_obj = NULL; + spin_unlock(&bdev->fence_lock); kref_init(&fbo->list_kref); kref_init(&fbo->kref); fbo->destroy = &ttm_transfered_destroy; @@ -661,13 +671,11 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, */ set_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); - - /* ttm_buffer_object_transfer accesses bo->sync_obj */ - ret = ttm_buffer_object_transfer(bo, &ghost_obj); spin_unlock(&bdev->fence_lock); if (tmp_obj) driver->sync_obj_unref(&tmp_obj); + ret = ttm_buffer_object_transfer(bo, &ghost_obj); if (ret) return ret; diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 74705f32..3df9f16 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -147,7 +147,7 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) + bo->vm_node->start - vma->vm_pgoff; - page_last = ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + + page_last = vma_pages(vma) + bo->vm_node->start - vma->vm_pgoff; if (unlikely(page_offset >= bo->num_pages)) { @@ -258,7 +258,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, read_lock(&bdev->vm_lock); bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff, - (vma->vm_end - vma->vm_start) >> PAGE_SHIFT); + vma_pages(vma)); if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref)) bo = NULL; read_unlock(&bdev->vm_lock); diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 7d759a4..5e93a52 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -296,7 +296,7 @@ int ttm_tt_swapin(struct ttm_tt *ttm) swap_storage = ttm->swap_storage; BUG_ON(swap_storage == NULL); - swap_space = swap_storage->f_path.dentry->d_inode->i_mapping; + swap_space = file_inode(swap_storage)->i_mapping; for (i = 0; i < ttm->num_pages; ++i) { from_page = shmem_read_mapping_page(swap_space, i); @@ -345,7 +345,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage) } else swap_storage = persistent_swap_storage; - swap_space = swap_storage->f_path.dentry->d_inode->i_mapping; + swap_space = file_inode(swap_storage)->i_mapping; for (i = 0; i < ttm->num_pages; ++i) { from_page = ttm->pages[i]; diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig index 56e0bf3..6222af1 100644 --- a/drivers/gpu/drm/udl/Kconfig +++ b/drivers/gpu/drm/udl/Kconfig @@ -1,6 +1,6 @@ config DRM_UDL tristate "DisplayLink" - depends on DRM && EXPERIMENTAL + depends on DRM depends on USB_ARCH_HAS_HCD select DRM_USB select FB_SYS_FILLRECT diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index afd212c..3816270 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -137,7 +137,7 @@ static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask) if (obj->pages == NULL) return -ENOMEM; - inode = obj->base.filp->f_path.dentry->d_inode; + inode = file_inode(obj->base.filp); mapping = inode->i_mapping; gfpmask |= mapping_gfp_mask(mapping); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c index c0f1cc7..d0ab3fb 100644 --- a/drivers/gpu/drm/via/via_map.c +++ b/drivers/gpu/drm/via/via_map.c @@ -120,7 +120,6 @@ int via_driver_unload(struct drm_device *dev) { drm_via_private_t *dev_priv = dev->dev_private; - idr_remove_all(&dev_priv->object_idr); idr_destroy(&dev_priv->object_idr); kfree(dev_priv); diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c index 0d55432..0ab93ff 100644 --- a/drivers/gpu/drm/via/via_mm.c +++ b/drivers/gpu/drm/via/via_mm.c @@ -148,17 +148,10 @@ int via_mem_alloc(struct drm_device *dev, void *data, if (retval) goto fail_alloc; -again: - if (idr_pre_get(&dev_priv->object_idr, GFP_KERNEL) == 0) { - retval = -ENOMEM; - goto fail_idr; - } - - retval = idr_get_new_above(&dev_priv->object_idr, item, 1, &user_key); - if (retval == -EAGAIN) - goto again; - if (retval) + retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); + if (retval < 0) goto fail_idr; + user_key = retval; list_add(&item->owner_list, &file_priv->obj_list); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 1655617..bc78425 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -177,17 +177,16 @@ int vmw_resource_alloc_id(struct vmw_resource *res) BUG_ON(res->id != -1); - do { - if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) - return -ENOMEM; - - write_lock(&dev_priv->resource_lock); - ret = idr_get_new_above(idr, res, 1, &res->id); - write_unlock(&dev_priv->resource_lock); + idr_preload(GFP_KERNEL); + write_lock(&dev_priv->resource_lock); - } while (ret == -EAGAIN); + ret = idr_alloc(idr, res, 1, 0, GFP_NOWAIT); + if (ret >= 0) + res->id = ret; - return ret; + write_unlock(&dev_priv->resource_lock); + idr_preload_end(); + return ret < 0 ? ret : 0; } /** |