diff options
Diffstat (limited to 'drivers')
143 files changed, 6330 insertions, 1836 deletions
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index aef87fdbd187..44311296ec02 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -840,6 +840,14 @@ static bool i830_check_flags(unsigned int flags) return false; } +void intel_gtt_insert_page(dma_addr_t addr, + unsigned int pg, + unsigned int flags) +{ + intel_private.driver->write_entry(addr, pg, flags); +} +EXPORT_SYMBOL(intel_gtt_insert_page); + void intel_gtt_insert_sg_entries(struct sg_table *st, unsigned int pg_start, unsigned int flags) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 6355ab38d630..20ce0687b111 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -824,7 +824,7 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) EXPORT_SYMBOL_GPL(dma_buf_vunmap); #ifdef CONFIG_DEBUG_FS -static int dma_buf_describe(struct seq_file *s) +static int dma_buf_debug_show(struct seq_file *s, void *unused) { int ret; struct dma_buf *buf_obj; @@ -879,17 +879,9 @@ static int dma_buf_describe(struct seq_file *s) return 0; } -static int dma_buf_show(struct seq_file *s, void *unused) -{ - void (*func)(struct seq_file *) = s->private; - - func(s); - return 0; -} - static int dma_buf_debug_open(struct inode *inode, struct file *file) { - return single_open(file, dma_buf_show, inode->i_private); + return single_open(file, dma_buf_debug_show, NULL); } static const struct file_operations dma_buf_debug_fops = { @@ -903,20 +895,23 @@ static struct dentry *dma_buf_debugfs_dir; static int dma_buf_init_debugfs(void) { + struct dentry *d; int err = 0; - dma_buf_debugfs_dir = debugfs_create_dir("dma_buf", NULL); - - if (IS_ERR(dma_buf_debugfs_dir)) { - err = PTR_ERR(dma_buf_debugfs_dir); - dma_buf_debugfs_dir = NULL; - return err; - } + d = debugfs_create_dir("dma_buf", NULL); + if (IS_ERR(d)) + return PTR_ERR(d); - err = dma_buf_debugfs_create_file("bufinfo", dma_buf_describe); + dma_buf_debugfs_dir = d; - if (err) + d = debugfs_create_file("bufinfo", S_IRUGO, dma_buf_debugfs_dir, + NULL, &dma_buf_debug_fops); + if (IS_ERR(d)) { pr_debug("dma_buf: debugfs: failed to create node bufinfo\n"); + debugfs_remove_recursive(dma_buf_debugfs_dir); + dma_buf_debugfs_dir = NULL; + err = PTR_ERR(d); + } return err; } @@ -926,17 +921,6 @@ static void dma_buf_uninit_debugfs(void) if (dma_buf_debugfs_dir) debugfs_remove_recursive(dma_buf_debugfs_dir); } - -int dma_buf_debugfs_create_file(const char *name, - int (*write)(struct seq_file *)) -{ - struct dentry *d; - - d = debugfs_create_file(name, S_IRUGO, dma_buf_debugfs_dir, - write, &dma_buf_debug_fops); - - return PTR_ERR_OR_ZERO(d); -} #else static inline int dma_buf_init_debugfs(void) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index ec4036a09f3e..a625b9137da2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -187,12 +187,12 @@ int init_pipelines(struct device_queue_manager *dqm, unsigned int get_first_pipe(struct device_queue_manager *dqm); unsigned int get_pipes_num(struct device_queue_manager *dqm); -extern inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) +static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) { return (pdd->lds_base >> 16) & 0xFF; } -extern inline unsigned int +static inline unsigned int get_sh_mem_bases_nybble_64(struct kfd_process_device *pdd) { return (pdd->lds_base >> 60) & 0x0E; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index d0d5f4baf72d..80113c335966 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -617,10 +617,7 @@ int kgd2kfd_resume(struct kfd_dev *kfd); int kfd_init_apertures(struct kfd_process *process); /* Queue Context Management */ -inline uint32_t lower_32(uint64_t x); -inline uint32_t upper_32(uint64_t x); struct cik_sdma_rlc_registers *get_sdma_mqd(void *mqd); -inline uint32_t get_sdma_base_addr(struct cik_sdma_rlc_registers *m); int init_queue(struct queue **q, struct queue_properties properties); void uninit_queue(struct queue *q); diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 381c5fcbf903..ccbdadb108dc 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -211,15 +211,8 @@ static int arcpgu_probe(struct platform_device *pdev) if (ret) goto err_unload; - ret = drm_connector_register_all(drm); - if (ret) - goto err_unregister; - return 0; -err_unregister: - drm_dev_unregister(drm); - err_unload: arcpgu_unload(drm); @@ -233,7 +226,6 @@ static int arcpgu_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); - drm_connector_unregister_all(drm); drm_dev_unregister(drm); arcpgu_unload(drm); drm_dev_unref(drm); diff --git a/drivers/gpu/drm/arm/Kconfig b/drivers/gpu/drm/arm/Kconfig index eaed454e043c..1b2906568a48 100644 --- a/drivers/gpu/drm/arm/Kconfig +++ b/drivers/gpu/drm/arm/Kconfig @@ -25,3 +25,19 @@ config DRM_HDLCD_SHOW_UNDERRUN Enable this option to show in red colour the pixels that the HDLCD device did not fetch from framebuffer due to underrun conditions. + +config DRM_MALI_DISPLAY + tristate "ARM Mali Display Processor" + depends on DRM && OF && (ARM || ARM64) + depends on COMMON_CLK + select DRM_ARM + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + select VIDEOMODE_HELPERS + help + Choose this option if you want to compile the ARM Mali Display + Processor driver. It supports the DP500, DP550 and DP650 variants + of the hardware. + + If compiled as a module it will be called mali-dp. diff --git a/drivers/gpu/drm/arm/Makefile b/drivers/gpu/drm/arm/Makefile index 89dcb7bab93a..bb8b158ff90d 100644 --- a/drivers/gpu/drm/arm/Makefile +++ b/drivers/gpu/drm/arm/Makefile @@ -1,2 +1,4 @@ hdlcd-y := hdlcd_drv.o hdlcd_crtc.o obj-$(CONFIG_DRM_HDLCD) += hdlcd.o +mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o +obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c new file mode 100644 index 000000000000..08e6a71f5d05 --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_crtc.c @@ -0,0 +1,216 @@ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Liviu Dudau <Liviu.Dudau@arm.com> + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP500/DP550/DP650 driver (crtc operations) + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <linux/clk.h> +#include <video/videomode.h> + +#include "malidp_drv.h" +#include "malidp_hw.h" + +static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct malidp_drm *malidp = crtc_to_malidp_device(crtc); + struct malidp_hw_device *hwdev = malidp->dev; + + /* + * check that the hardware can drive the required clock rate, + * but skip the check if the clock is meant to be disabled (req_rate = 0) + */ + long rate, req_rate = mode->crtc_clock * 1000; + + if (req_rate) { + rate = clk_round_rate(hwdev->mclk, req_rate); + if (rate < req_rate) { + DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n", + mode->crtc_clock); + return false; + } + + rate = clk_round_rate(hwdev->pxlclk, req_rate); + if (rate != req_rate) { + DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n", + req_rate); + return false; + } + } + + return true; +} + +static void malidp_crtc_enable(struct drm_crtc *crtc) +{ + struct malidp_drm *malidp = crtc_to_malidp_device(crtc); + struct malidp_hw_device *hwdev = malidp->dev; + struct videomode vm; + + drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm); + + clk_prepare_enable(hwdev->pxlclk); + + /* mclk needs to be set to the same or higher rate than pxlclk */ + clk_set_rate(hwdev->mclk, crtc->state->adjusted_mode.crtc_clock * 1000); + clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000); + + hwdev->modeset(hwdev, &vm); + hwdev->leave_config_mode(hwdev); + drm_crtc_vblank_on(crtc); +} + +static void malidp_crtc_disable(struct drm_crtc *crtc) +{ + struct malidp_drm *malidp = crtc_to_malidp_device(crtc); + struct malidp_hw_device *hwdev = malidp->dev; + + drm_crtc_vblank_off(crtc); + hwdev->enter_config_mode(hwdev); + clk_disable_unprepare(hwdev->pxlclk); +} + +static int malidp_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct malidp_drm *malidp = crtc_to_malidp_device(crtc); + struct malidp_hw_device *hwdev = malidp->dev; + struct drm_plane *plane; + const struct drm_plane_state *pstate; + u32 rot_mem_free, rot_mem_usable; + int rotated_planes = 0; + + /* + * check if there is enough rotation memory available for planes + * that need 90° and 270° rotation. Each plane has set its required + * memory size in the ->plane_check() callback, here we only make + * sure that the sums are less that the total usable memory. + * + * The rotation memory allocation algorithm (for each plane): + * a. If no more rotated planes exist, all remaining rotate + * memory in the bank is available for use by the plane. + * b. If other rotated planes exist, and plane's layer ID is + * DE_VIDEO1, it can use all the memory from first bank if + * secondary rotation memory bank is available, otherwise it can + * use up to half the bank's memory. + * c. If other rotated planes exist, and plane's layer ID is not + * DE_VIDEO1, it can use half of the available memory + * + * Note: this algorithm assumes that the order in which the planes are + * checked always has DE_VIDEO1 plane first in the list if it is + * rotated. Because that is how we create the planes in the first + * place, under current DRM version things work, but if ever the order + * in which drm_atomic_crtc_state_for_each_plane() iterates over planes + * changes, we need to pre-sort the planes before validation. + */ + + /* first count the number of rotated planes */ + drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { + if (pstate->rotation & MALIDP_ROTATED_MASK) + rotated_planes++; + } + + rot_mem_free = hwdev->rotation_memory[0]; + /* + * if we have more than 1 plane using rotation memory, use the second + * block of rotation memory as well + */ + if (rotated_planes > 1) + rot_mem_free += hwdev->rotation_memory[1]; + + /* now validate the rotation memory requirements */ + drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { + struct malidp_plane *mp = to_malidp_plane(plane); + struct malidp_plane_state *ms = to_malidp_plane_state(pstate); + + if (pstate->rotation & MALIDP_ROTATED_MASK) { + /* process current plane */ + rotated_planes--; + + if (!rotated_planes) { + /* no more rotated planes, we can use what's left */ + rot_mem_usable = rot_mem_free; + } else { + if ((mp->layer->id != DE_VIDEO1) || + (hwdev->rotation_memory[1] == 0)) + rot_mem_usable = rot_mem_free / 2; + else + rot_mem_usable = hwdev->rotation_memory[0]; + } + + rot_mem_free -= rot_mem_usable; + + if (ms->rotmem_size > rot_mem_usable) + return -EINVAL; + } + } + + return 0; +} + +static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = { + .mode_fixup = malidp_crtc_mode_fixup, + .enable = malidp_crtc_enable, + .disable = malidp_crtc_disable, + .atomic_check = malidp_crtc_atomic_check, +}; + +static const struct drm_crtc_funcs malidp_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +int malidp_crtc_init(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + struct drm_plane *primary = NULL, *plane; + int ret; + + ret = malidp_de_planes_init(drm); + if (ret < 0) { + DRM_ERROR("Failed to initialise planes\n"); + return ret; + } + + drm_for_each_plane(plane, drm) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + primary = plane; + break; + } + } + + if (!primary) { + DRM_ERROR("no primary plane found\n"); + ret = -EINVAL; + goto crtc_cleanup_planes; + } + + ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL, + &malidp_crtc_funcs, NULL); + + if (!ret) { + drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs); + return 0; + } + +crtc_cleanup_planes: + malidp_de_planes_destroy(drm); + + return ret; +} diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c new file mode 100644 index 000000000000..e5b44e92f8cf --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -0,0 +1,512 @@ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Liviu Dudau <Liviu.Dudau@arm.com> + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP500/DP550/DP650 KMS/DRM driver + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/of_reserved_mem.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_of.h> + +#include "malidp_drv.h" +#include "malidp_regs.h" +#include "malidp_hw.h" + +#define MALIDP_CONF_VALID_TIMEOUT 250 + +/* + * set the "config valid" bit and wait until the hardware acts on it + */ +static int malidp_set_and_wait_config_valid(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + int ret; + + hwdev->set_config_valid(hwdev); + /* don't wait for config_valid flag if we are in config mode */ + if (hwdev->in_config_mode(hwdev)) + return 0; + + ret = wait_event_interruptible_timeout(malidp->wq, + atomic_read(&malidp->config_valid) == 1, + msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); + + return (ret > 0) ? 0 : -ETIMEDOUT; +} + +static void malidp_output_poll_changed(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + + drm_fbdev_cma_hotplug_event(malidp->fbdev); +} + +static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) +{ + struct drm_pending_vblank_event *event; + struct drm_device *drm = state->dev; + struct malidp_drm *malidp = drm->dev_private; + int ret = malidp_set_and_wait_config_valid(drm); + + if (ret) + DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); + + event = malidp->crtc.state->event; + if (event) { + malidp->crtc.state->event = NULL; + + spin_lock_irq(&drm->event_lock); + if (drm_crtc_vblank_get(&malidp->crtc) == 0) + drm_crtc_arm_vblank_event(&malidp->crtc, event); + else + drm_crtc_send_vblank_event(&malidp->crtc, event); + spin_unlock_irq(&drm->event_lock); + } + drm_atomic_helper_commit_hw_done(state); +} + +static void malidp_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *drm = state->dev; + + drm_atomic_helper_commit_modeset_disables(drm, state); + drm_atomic_helper_commit_modeset_enables(drm, state); + drm_atomic_helper_commit_planes(drm, state, true); + + malidp_atomic_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(drm, state); + + drm_atomic_helper_cleanup_planes(drm, state); +} + +static struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { + .atomic_commit_tail = malidp_atomic_commit_tail, +}; + +static const struct drm_mode_config_funcs malidp_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + .output_poll_changed = malidp_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int malidp_enable_vblank(struct drm_device *drm, unsigned int crtc) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + + malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK, + hwdev->map.de_irq_map.vsync_irq); + return 0; +} + +static void malidp_disable_vblank(struct drm_device *drm, unsigned int pipe) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + + malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, + hwdev->map.de_irq_map.vsync_irq); +} + +static int malidp_init(struct drm_device *drm) +{ + int ret; + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + + drm_mode_config_init(drm); + + drm->mode_config.min_width = hwdev->min_line_size; + drm->mode_config.min_height = hwdev->min_line_size; + drm->mode_config.max_width = hwdev->max_line_size; + drm->mode_config.max_height = hwdev->max_line_size; + drm->mode_config.funcs = &malidp_mode_config_funcs; + drm->mode_config.helper_private = &malidp_mode_config_helpers; + + ret = malidp_crtc_init(drm); + if (ret) { + drm_mode_config_cleanup(drm); + return ret; + } + + return 0; +} + +static int malidp_irq_init(struct platform_device *pdev) +{ + int irq_de, irq_se, ret = 0; + struct drm_device *drm = dev_get_drvdata(&pdev->dev); + + /* fetch the interrupts from DT */ + irq_de = platform_get_irq_byname(pdev, "DE"); + if (irq_de < 0) { + DRM_ERROR("no 'DE' IRQ specified!\n"); + return irq_de; + } + irq_se = platform_get_irq_byname(pdev, "SE"); + if (irq_se < 0) { + DRM_ERROR("no 'SE' IRQ specified!\n"); + return irq_se; + } + + ret = malidp_de_irq_init(drm, irq_de); + if (ret) + return ret; + + ret = malidp_se_irq_init(drm, irq_se); + if (ret) { + malidp_de_irq_fini(drm); + return ret; + } + + return 0; +} + +static void malidp_lastclose(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + + drm_fbdev_cma_restore_mode(malidp->fbdev); +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .llseek = noop_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static struct drm_driver malidp_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | + DRIVER_PRIME, + .lastclose = malidp_lastclose, + .get_vblank_counter = drm_vblank_no_hw_counter, + .enable_vblank = malidp_enable_vblank, + .disable_vblank = malidp_disable_vblank, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .fops = &fops, + .name = "mali-dp", + .desc = "ARM Mali Display Processor driver", + .date = "20160106", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id malidp_drm_of_match[] = { + { + .compatible = "arm,mali-dp500", + .data = &malidp_device[MALIDP_500] + }, + { + .compatible = "arm,mali-dp550", + .data = &malidp_device[MALIDP_550] + }, + { + .compatible = "arm,mali-dp650", + .data = &malidp_device[MALIDP_650] + }, + {}, +}; +MODULE_DEVICE_TABLE(of, malidp_drm_of_match); + +#define MAX_OUTPUT_CHANNELS 3 + +static int malidp_bind(struct device *dev) +{ + struct resource *res; + struct drm_device *drm; + struct malidp_drm *malidp; + struct malidp_hw_device *hwdev; + struct platform_device *pdev = to_platform_device(dev); + /* number of lines for the R, G and B output */ + u8 output_width[MAX_OUTPUT_CHANNELS]; + int ret = 0, i; + u32 version, out_depth = 0; + + malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL); + if (!malidp) + return -ENOMEM; + + hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL); + if (!hwdev) + return -ENOMEM; + + /* + * copy the associated data from malidp_drm_of_match to avoid + * having to keep a reference to the OF node after binding + */ + memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev)); + malidp->dev = hwdev; + + INIT_LIST_HEAD(&malidp->event_list); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hwdev->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(hwdev->regs)) { + DRM_ERROR("Failed to map control registers area\n"); + return PTR_ERR(hwdev->regs); + } + + hwdev->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(hwdev->pclk)) + return PTR_ERR(hwdev->pclk); + + hwdev->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(hwdev->aclk)) + return PTR_ERR(hwdev->aclk); + + hwdev->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(hwdev->mclk)) + return PTR_ERR(hwdev->mclk); + + hwdev->pxlclk = devm_clk_get(dev, "pxlclk"); + if (IS_ERR(hwdev->pxlclk)) + return PTR_ERR(hwdev->pxlclk); + + /* Get the optional framebuffer memory resource */ + ret = of_reserved_mem_device_init(dev); + if (ret && ret != -ENODEV) + return ret; + + drm = drm_dev_alloc(&malidp_driver, dev); + if (!drm) { + ret = -ENOMEM; + goto alloc_fail; + } + + /* Enable APB clock in order to get access to the registers */ + clk_prepare_enable(hwdev->pclk); + /* + * Enable AXI clock and main clock so that prefetch can start once + * the registers are set + */ + clk_prepare_enable(hwdev->aclk); + clk_prepare_enable(hwdev->mclk); + + ret = hwdev->query_hw(hwdev); + if (ret) { + DRM_ERROR("Invalid HW configuration\n"); + goto query_hw_fail; + } + + version = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_DE_CORE_ID); + DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16, + (version >> 12) & 0xf, (version >> 8) & 0xf); + + /* set the number of lines used for output of RGB data */ + ret = of_property_read_u8_array(dev->of_node, + "arm,malidp-output-port-lines", + output_width, MAX_OUTPUT_CHANNELS); + if (ret) + goto query_hw_fail; + + for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) + out_depth = (out_depth << 8) | (output_width[i] & 0xf); + malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base); + + drm->dev_private = malidp; + dev_set_drvdata(dev, drm); + atomic_set(&malidp->config_valid, 0); + init_waitqueue_head(&malidp->wq); + + ret = malidp_init(drm); + if (ret < 0) + goto init_fail; + + ret = drm_dev_register(drm, 0); + if (ret) + goto register_fail; + + /* Set the CRTC's port so that the encoder component can find it */ + malidp->crtc.port = of_graph_get_next_endpoint(dev->of_node, NULL); + + ret = component_bind_all(dev, drm); + of_node_put(malidp->crtc.port); + + if (ret) { + DRM_ERROR("Failed to bind all components\n"); + goto bind_fail; + } + + ret = malidp_irq_init(pdev); + if (ret < 0) + goto irq_init_fail; + + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret < 0) { + DRM_ERROR("failed to initialise vblank\n"); + goto vblank_fail; + } + + drm_mode_config_reset(drm); + + malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + drm->mode_config.num_connector); + + if (IS_ERR(malidp->fbdev)) { + ret = PTR_ERR(malidp->fbdev); + malidp->fbdev = NULL; + goto fbdev_fail; + } + + drm_kms_helper_poll_init(drm); + return 0; + +fbdev_fail: + drm_vblank_cleanup(drm); +vblank_fail: + malidp_se_irq_fini(drm); + malidp_de_irq_fini(drm); +irq_init_fail: + component_unbind_all(dev, drm); +bind_fail: + drm_dev_unregister(drm); +register_fail: + malidp_de_planes_destroy(drm); + drm_mode_config_cleanup(drm); +init_fail: + drm->dev_private = NULL; + dev_set_drvdata(dev, NULL); +query_hw_fail: + clk_disable_unprepare(hwdev->mclk); + clk_disable_unprepare(hwdev->aclk); + clk_disable_unprepare(hwdev->pclk); + drm_dev_unref(drm); +alloc_fail: + of_reserved_mem_device_release(dev); + + return ret; +} + +static void malidp_unbind(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + + if (malidp->fbdev) { + drm_fbdev_cma_fini(malidp->fbdev); + malidp->fbdev = NULL; + } + drm_kms_helper_poll_fini(drm); + malidp_se_irq_fini(drm); + malidp_de_irq_fini(drm); + drm_vblank_cleanup(drm); + component_unbind_all(dev, drm); + drm_dev_unregister(drm); + malidp_de_planes_destroy(drm); + drm_mode_config_cleanup(drm); + drm->dev_private = NULL; + dev_set_drvdata(dev, NULL); + clk_disable_unprepare(hwdev->mclk); + clk_disable_unprepare(hwdev->aclk); + clk_disable_unprepare(hwdev->pclk); + drm_dev_unref(drm); + of_reserved_mem_device_release(dev); +} + +static const struct component_master_ops malidp_master_ops = { + .bind = malidp_bind, + .unbind = malidp_unbind, +}; + +static int malidp_compare_dev(struct device *dev, void *data) +{ + struct device_node *np = data; + + return dev->of_node == np; +} + +static int malidp_platform_probe(struct platform_device *pdev) +{ + struct device_node *port, *ep; + struct component_match *match = NULL; + + if (!pdev->dev.of_node) + return -ENODEV; + + /* there is only one output port inside each device, find it */ + ep = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); + if (!ep) + return -ENODEV; + + if (!of_device_is_available(ep)) { + of_node_put(ep); + return -ENODEV; + } + + /* add the remote encoder port as component */ + port = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (!port || !of_device_is_available(port)) { + of_node_put(port); + return -EAGAIN; + } + + component_match_add(&pdev->dev, &match, malidp_compare_dev, port); + return component_master_add_with_match(&pdev->dev, &malidp_master_ops, + match); +} + +static int malidp_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &malidp_master_ops); + return 0; +} + +static struct platform_driver malidp_platform_driver = { + .probe = malidp_platform_probe, + .remove = malidp_platform_remove, + .driver = { + .name = "mali-dp", + .of_match_table = malidp_drm_of_match, + }, +}; + +module_platform_driver(malidp_platform_driver); + +MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); +MODULE_DESCRIPTION("ARM Mali DP DRM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h new file mode 100644 index 000000000000..95558fde214b --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_drv.h @@ -0,0 +1,54 @@ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Liviu Dudau <Liviu.Dudau@arm.com> + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP500/DP550/DP650 KMS/DRM driver structures + */ + +#ifndef __MALIDP_DRV_H__ +#define __MALIDP_DRV_H__ + +#include <linux/mutex.h> +#include <linux/wait.h> +#include "malidp_hw.h" + +struct malidp_drm { + struct malidp_hw_device *dev; + struct drm_fbdev_cma *fbdev; + struct list_head event_list; + struct drm_crtc crtc; + wait_queue_head_t wq; + atomic_t config_valid; +}; + +#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc) + +struct malidp_plane { + struct drm_plane base; + struct malidp_hw_device *hwdev; + const struct malidp_layer *layer; +}; + +struct malidp_plane_state { + struct drm_plane_state base; + + /* size of the required rotation memory if plane is rotated */ + u32 rotmem_size; +}; + +#define to_malidp_plane(x) container_of(x, struct malidp_plane, base) +#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base) + +int malidp_de_planes_init(struct drm_device *drm); +void malidp_de_planes_destroy(struct drm_device *drm); +int malidp_crtc_init(struct drm_device *drm); + +/* often used combination of rotational bits */ +#define MALIDP_ROTATED_MASK (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)) + +#endif /* __MALIDP_DRV_H__ */ diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c new file mode 100644 index 000000000000..a6132f1d58c1 --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -0,0 +1,691 @@ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Liviu Dudau <Liviu.Dudau@arm.com> + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP500/DP550/DP650 hardware manipulation routines. This is where + * the difference between various versions of the hardware is being dealt with + * in an attempt to provide to the rest of the driver code a unified view + */ + +#include <linux/types.h> +#include <linux/io.h> +#include <drm/drmP.h> +#include <video/videomode.h> +#include <video/display_timing.h> + +#include "malidp_drv.h" +#include "malidp_hw.h" + +static const struct malidp_input_format malidp500_de_formats[] = { + /* fourcc, layers supporting the format, internal id */ + { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 }, + { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 1 }, + { DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 2 }, + { DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 3 }, + { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 4 }, + { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 5 }, + { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 6 }, + { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 7 }, + { DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 8 }, + { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 9 }, + { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 10 }, + { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 11 }, + { DRM_FORMAT_UYVY, DE_VIDEO1, 12 }, + { DRM_FORMAT_YUYV, DE_VIDEO1, 13 }, + { DRM_FORMAT_NV12, DE_VIDEO1, 14 }, + { DRM_FORMAT_YUV420, DE_VIDEO1, 15 }, +}; + +#define MALIDP_ID(__group, __format) \ + ((((__group) & 0x7) << 3) | ((__format) & 0x7)) + +#define MALIDP_COMMON_FORMATS \ + /* fourcc, layers supporting the format, internal id */ \ + { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 0) }, \ + { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 1) }, \ + { DRM_FORMAT_RGBA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 2) }, \ + { DRM_FORMAT_BGRA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 3) }, \ + { DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 0) }, \ + { DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 1) }, \ + { DRM_FORMAT_RGBA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 2) }, \ + { DRM_FORMAT_BGRA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 3) }, \ + { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 0) }, \ + { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 1) }, \ + { DRM_FORMAT_RGBX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 2) }, \ + { DRM_FORMAT_BGRX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 3) }, \ + { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 0) }, \ + { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 1) }, \ + { DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 0) }, \ + { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \ + { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \ + { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \ + { DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \ + { DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \ + { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \ + { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) } + +static const struct malidp_input_format malidp550_de_formats[] = { + MALIDP_COMMON_FORMATS, +}; + +static const struct malidp_layer malidp500_layers[] = { + { DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE }, + { DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE }, + { DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE }, +}; + +static const struct malidp_layer malidp550_layers[] = { + { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE }, + { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE }, + { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE }, + { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE }, +}; + +#define MALIDP_DE_DEFAULT_PREFETCH_START 5 + +static int malidp500_query_hw(struct malidp_hw_device *hwdev) +{ + u32 conf = malidp_hw_read(hwdev, MALIDP500_CONFIG_ID); + /* bit 4 of the CONFIG_ID register holds the line size multiplier */ + u8 ln_size_mult = conf & 0x10 ? 2 : 1; + + hwdev->min_line_size = 2; + hwdev->max_line_size = SZ_2K * ln_size_mult; + hwdev->rotation_memory[0] = SZ_1K * 64 * ln_size_mult; + hwdev->rotation_memory[1] = 0; /* no second rotation memory bank */ + + return 0; +} + +static void malidp500_enter_config_mode(struct malidp_hw_device *hwdev) +{ + u32 status, count = 100; + + malidp_hw_setbits(hwdev, MALIDP500_DC_CONFIG_REQ, MALIDP500_DC_CONTROL); + while (count) { + status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if ((status & MALIDP500_DC_CONFIG_REQ) == MALIDP500_DC_CONFIG_REQ) + break; + /* + * entering config mode can take as long as the rendering + * of a full frame, hence the long sleep here + */ + usleep_range(1000, 10000); + count--; + } + WARN(count == 0, "timeout while entering config mode"); +} + +static void malidp500_leave_config_mode(struct malidp_hw_device *hwdev) +{ + u32 status, count = 100; + + malidp_hw_clearbits(hwdev, MALIDP500_DC_CONFIG_REQ, MALIDP500_DC_CONTROL); + while (count) { + status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if ((status & MALIDP500_DC_CONFIG_REQ) == 0) + break; + usleep_range(100, 1000); + count--; + } + WARN(count == 0, "timeout while leaving config mode"); +} + +static bool malidp500_in_config_mode(struct malidp_hw_device *hwdev) +{ + u32 status; + + status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if ((status & MALIDP500_DC_CONFIG_REQ) == MALIDP500_DC_CONFIG_REQ) + return true; + + return false; +} + +static void malidp500_set_config_valid(struct malidp_hw_device *hwdev) +{ + malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP500_CONFIG_VALID); +} + +static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode *mode) +{ + u32 val = 0; + + malidp_hw_clearbits(hwdev, MALIDP500_DC_CLEAR_MASK, MALIDP500_DC_CONTROL); + if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) + val |= MALIDP500_HSYNCPOL; + if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH) + val |= MALIDP500_VSYNCPOL; + val |= MALIDP_DE_DEFAULT_PREFETCH_START; + malidp_hw_setbits(hwdev, val, MALIDP500_DC_CONTROL); + + /* + * Mali-DP500 encodes the background color like this: + * - red @ MALIDP500_BGND_COLOR[12:0] + * - green @ MALIDP500_BGND_COLOR[27:16] + * - blue @ (MALIDP500_BGND_COLOR + 4)[12:0] + */ + val = ((MALIDP_BGND_COLOR_G & 0xfff) << 16) | + (MALIDP_BGND_COLOR_R & 0xfff); + malidp_hw_write(hwdev, val, MALIDP500_BGND_COLOR); + malidp_hw_write(hwdev, MALIDP_BGND_COLOR_B, MALIDP500_BGND_COLOR + 4); + + val = MALIDP_DE_H_FRONTPORCH(mode->hfront_porch) | + MALIDP_DE_H_BACKPORCH(mode->hback_porch); + malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_H_TIMINGS); + + val = MALIDP500_DE_V_FRONTPORCH(mode->vfront_porch) | + MALIDP_DE_V_BACKPORCH(mode->vback_porch); + malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_V_TIMINGS); + + val = MALIDP_DE_H_SYNCWIDTH(mode->hsync_len) | + MALIDP_DE_V_SYNCWIDTH(mode->vsync_len); + malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_SYNC_WIDTH); + + val = MALIDP_DE_H_ACTIVE(mode->hactive) | MALIDP_DE_V_ACTIVE(mode->vactive); + malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_HV_ACTIVE); + + if (mode->flags & DISPLAY_FLAGS_INTERLACED) + malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC); + else + malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC); +} + +static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt) +{ + unsigned int depth; + int bpp; + + /* RGB888 or BGR888 can't be rotated */ + if ((fmt == DRM_FORMAT_RGB888) || (fmt == DRM_FORMAT_BGR888)) + return -EINVAL; + + /* + * Each layer needs enough rotation memory to fit 8 lines + * worth of pixel data. Required size is then: + * size = rotated_width * (bpp / 8) * 8; + */ + drm_fb_get_bpp_depth(fmt, &depth, &bpp); + + return w * bpp; +} + +static int malidp550_query_hw(struct malidp_hw_device *hwdev) +{ + u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID); + u8 ln_size = (conf >> 4) & 0x3, rsize; + + hwdev->min_line_size = 2; + + switch (ln_size) { + case 0: + hwdev->max_line_size = SZ_2K; + /* two banks of 64KB for rotation memory */ + rsize = 64; + break; + case 1: + hwdev->max_line_size = SZ_4K; + /* two banks of 128KB for rotation memory */ + rsize = 128; + break; + case 2: + hwdev->max_line_size = 1280; + /* two banks of 40KB for rotation memory */ + rsize = 40; + break; + case 3: + /* reserved value */ + hwdev->max_line_size = 0; + return -EINVAL; + } + + hwdev->rotation_memory[0] = hwdev->rotation_memory[1] = rsize * SZ_1K; + return 0; +} + +static void malidp550_enter_config_mode(struct malidp_hw_device *hwdev) +{ + u32 status, count = 100; + + malidp_hw_setbits(hwdev, MALIDP550_DC_CONFIG_REQ, MALIDP550_DC_CONTROL); + while (count) { + status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if ((status & MALIDP550_DC_CONFIG_REQ) == MALIDP550_DC_CONFIG_REQ) + break; + /* + * entering config mode can take as long as the rendering + * of a full frame, hence the long sleep here + */ + usleep_range(1000, 10000); + count--; + } + WARN(count == 0, "timeout while entering config mode"); +} + +static void malidp550_leave_config_mode(struct malidp_hw_device *hwdev) +{ + u32 status, count = 100; + + malidp_hw_clearbits(hwdev, MALIDP550_DC_CONFIG_REQ, MALIDP550_DC_CONTROL); + while (count) { + status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if ((status & MALIDP550_DC_CONFIG_REQ) == 0) + break; + usleep_range(100, 1000); + count--; + } + WARN(count == 0, "timeout while leaving config mode"); +} + +static bool malidp550_in_config_mode(struct malidp_hw_device *hwdev) +{ + u32 status; + + status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if ((status & MALIDP550_DC_CONFIG_REQ) == MALIDP550_DC_CONFIG_REQ) + return true; + + return false; +} + +static void malidp550_set_config_valid(struct malidp_hw_device *hwdev) +{ + malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP550_CONFIG_VALID); +} + +static void malidp550_modeset(struct malidp_hw_device *hwdev, struct videomode *mode) +{ + u32 val = MALIDP_DE_DEFAULT_PREFETCH_START; + + malidp_hw_write(hwdev, val, MALIDP550_DE_CONTROL); + /* + * Mali-DP550 and Mali-DP650 encode the background color like this: + * - red @ MALIDP550_DE_BGND_COLOR[23:16] + * - green @ MALIDP550_DE_BGND_COLOR[15:8] + * - blue @ MALIDP550_DE_BGND_COLOR[7:0] + * + * We need to truncate the least significant 4 bits from the default + * MALIDP_BGND_COLOR_x values + */ + val = (((MALIDP_BGND_COLOR_R >> 4) & 0xff) << 16) | + (((MALIDP_BGND_COLOR_G >> 4) & 0xff) << 8) | + ((MALIDP_BGND_COLOR_B >> 4) & 0xff); + malidp_hw_write(hwdev, val, MALIDP550_DE_BGND_COLOR); + + val = MALIDP_DE_H_FRONTPORCH(mode->hfront_porch) | + MALIDP_DE_H_BACKPORCH(mode->hback_porch); + malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_H_TIMINGS); + + val = MALIDP550_DE_V_FRONTPORCH(mode->vfront_porch) | + MALIDP_DE_V_BACKPORCH(mode->vback_porch); + malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_V_TIMINGS); + + val = MALIDP_DE_H_SYNCWIDTH(mode->hsync_len) | + MALIDP_DE_V_SYNCWIDTH(mode->vsync_len); + if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) + val |= MALIDP550_HSYNCPOL; + if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH) + val |= MALIDP550_VSYNCPOL; + malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_SYNC_WIDTH); + + val = MALIDP_DE_H_ACTIVE(mode->hactive) | MALIDP_DE_V_ACTIVE(mode->vactive); + malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_HV_ACTIVE); + + if (mode->flags & DISPLAY_FLAGS_INTERLACED) + malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC); + else + malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC); +} + +static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt) +{ + u32 bytes_per_col; + + /* raw RGB888 or BGR888 can't be rotated */ + if ((fmt == DRM_FORMAT_RGB888) || (fmt == DRM_FORMAT_BGR888)) + return -EINVAL; + + switch (fmt) { + /* 8 lines at 4 bytes per pixel */ + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + /* 16 lines at 2 bytes per pixel */ + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_YUYV: + bytes_per_col = 32; + break; + /* 16 lines at 1.5 bytes per pixel */ + case DRM_FORMAT_NV12: + case DRM_FORMAT_YUV420: + bytes_per_col = 24; + break; + default: + return -EINVAL; + } + + return w * bytes_per_col; +} + +static int malidp650_query_hw(struct malidp_hw_device *hwdev) +{ + u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID); + u8 ln_size = (conf >> 4) & 0x3, rsize; + + hwdev->min_line_size = 4; + + switch (ln_size) { + case 0: + case 2: + /* reserved values */ + hwdev->max_line_size = 0; + return -EINVAL; + case 1: + hwdev->max_line_size = SZ_4K; + /* two banks of 128KB for rotation memory */ + rsize = 128; + break; + case 3: + hwdev->max_line_size = 2560; + /* two banks of 80KB for rotation memory */ + rsize = 80; + } + + hwdev->rotation_memory[0] = hwdev->rotation_memory[1] = rsize * SZ_1K; + return 0; +} + +const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { + [MALIDP_500] = { + .map = { + .se_base = MALIDP500_SE_BASE, + .dc_base = MALIDP500_DC_BASE, + .out_depth_base = MALIDP500_OUTPUT_DEPTH, + .features = 0, /* no CLEARIRQ register */ + .n_layers = ARRAY_SIZE(malidp500_layers), + .layers = malidp500_layers, + .de_irq_map = { + .irq_mask = MALIDP_DE_IRQ_UNDERRUN | + MALIDP500_DE_IRQ_AXI_ERR | + MALIDP500_DE_IRQ_VSYNC | + MALIDP500_DE_IRQ_GLOBAL, + .vsync_irq = MALIDP500_DE_IRQ_VSYNC, + }, + .se_irq_map = { + .irq_mask = MALIDP500_SE_IRQ_CONF_MODE, + .vsync_irq = 0, + }, + .dc_irq_map = { + .irq_mask = MALIDP500_DE_IRQ_CONF_VALID, + .vsync_irq = MALIDP500_DE_IRQ_CONF_VALID, + }, + .input_formats = malidp500_de_formats, + .n_input_formats = ARRAY_SIZE(malidp500_de_formats), + }, + .query_hw = malidp500_query_hw, + .enter_config_mode = malidp500_enter_config_mode, + .leave_config_mode = malidp500_leave_config_mode, + .in_config_mode = malidp500_in_config_mode, + .set_config_valid = malidp500_set_config_valid, + .modeset = malidp500_modeset, + .rotmem_required = malidp500_rotmem_required, + }, + [MALIDP_550] = { + .map = { + .se_base = MALIDP550_SE_BASE, + .dc_base = MALIDP550_DC_BASE, + .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH, + .features = MALIDP_REGMAP_HAS_CLEARIRQ, + .n_layers = ARRAY_SIZE(malidp550_layers), + .layers = malidp550_layers, + .de_irq_map = { + .irq_mask = MALIDP_DE_IRQ_UNDERRUN | + MALIDP550_DE_IRQ_VSYNC, + .vsync_irq = MALIDP550_DE_IRQ_VSYNC, + }, + .se_irq_map = { + .irq_mask = MALIDP550_SE_IRQ_EOW | + MALIDP550_SE_IRQ_AXI_ERR, + }, + .dc_irq_map = { + .irq_mask = MALIDP550_DC_IRQ_CONF_VALID, + .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, + }, + .input_formats = malidp550_de_formats, + .n_input_formats = ARRAY_SIZE(malidp550_de_formats), + }, + .query_hw = malidp550_query_hw, + .enter_config_mode = malidp550_enter_config_mode, + .leave_config_mode = malidp550_leave_config_mode, + .in_config_mode = malidp550_in_config_mode, + .set_config_valid = malidp550_set_config_valid, + .modeset = malidp550_modeset, + .rotmem_required = malidp550_rotmem_required, + }, + [MALIDP_650] = { + .map = { + .se_base = MALIDP550_SE_BASE, + .dc_base = MALIDP550_DC_BASE, + .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH, + .features = MALIDP_REGMAP_HAS_CLEARIRQ, + .n_layers = ARRAY_SIZE(malidp550_layers), + .layers = malidp550_layers, + .de_irq_map = { + .irq_mask = MALIDP_DE_IRQ_UNDERRUN | + MALIDP650_DE_IRQ_DRIFT | + MALIDP550_DE_IRQ_VSYNC, + .vsync_irq = MALIDP550_DE_IRQ_VSYNC, + }, + .se_irq_map = { + .irq_mask = MALIDP550_SE_IRQ_EOW | + MALIDP550_SE_IRQ_AXI_ERR, + }, + .dc_irq_map = { + .irq_mask = MALIDP550_DC_IRQ_CONF_VALID, + .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, + }, + .input_formats = malidp550_de_formats, + .n_input_formats = ARRAY_SIZE(malidp550_de_formats), + }, + .query_hw = malidp650_query_hw, + .enter_config_mode = malidp550_enter_config_mode, + .leave_config_mode = malidp550_leave_config_mode, + .in_config_mode = malidp550_in_config_mode, + .set_config_valid = malidp550_set_config_valid, + .modeset = malidp550_modeset, + .rotmem_required = malidp550_rotmem_required, + }, +}; + +u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, + u8 layer_id, u32 format) +{ + unsigned int i; + + for (i = 0; i < map->n_input_formats; i++) { + if (((map->input_formats[i].layer & layer_id) == layer_id) && + (map->input_formats[i].format == format)) + return map->input_formats[i].id; + } + + return MALIDP_INVALID_FORMAT_ID; +} + +static void malidp_hw_clear_irq(struct malidp_hw_device *hwdev, u8 block, u32 irq) +{ + u32 base = malidp_get_block_base(hwdev, block); + + if (hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) + malidp_hw_write(hwdev, irq, base + MALIDP_REG_CLEARIRQ); + else + malidp_hw_write(hwdev, irq, base + MALIDP_REG_STATUS); +} + +static irqreturn_t malidp_de_irq(int irq, void *arg) +{ + struct drm_device *drm = arg; + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev; + const struct malidp_irq_map *de; + u32 status, mask, dc_status; + irqreturn_t ret = IRQ_NONE; + + if (!drm->dev_private) + return IRQ_HANDLED; + + hwdev = malidp->dev; + de = &hwdev->map.de_irq_map; + + /* first handle the config valid IRQ */ + dc_status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS); + if (dc_status & hwdev->map.dc_irq_map.vsync_irq) { + /* we have a page flip event */ + atomic_set(&malidp->config_valid, 1); + malidp_hw_clear_irq(hwdev, MALIDP_DC_BLOCK, dc_status); + ret = IRQ_WAKE_THREAD; + } + + status = malidp_hw_read(hwdev, MALIDP_REG_STATUS); + if (!(status & de->irq_mask)) + return ret; + + mask = malidp_hw_read(hwdev, MALIDP_REG_MASKIRQ); + status &= mask; + if (status & de->vsync_irq) + drm_crtc_handle_vblank(&malidp->crtc); + + malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, status); + + return (ret == IRQ_NONE) ? IRQ_HANDLED : ret; +} + +static irqreturn_t malidp_de_irq_thread_handler(int irq, void *arg) +{ + struct drm_device *drm = arg; + struct malidp_drm *malidp = drm->dev_private; + + wake_up(&malidp->wq); + + return IRQ_HANDLED; +} + +int malidp_de_irq_init(struct drm_device *drm, int irq) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + int ret; + + /* ensure interrupts are disabled */ + malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff); + malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff); + malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff); + malidp_hw_clear_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff); + + ret = devm_request_threaded_irq(drm->dev, irq, malidp_de_irq, + malidp_de_irq_thread_handler, + IRQF_SHARED, "malidp-de", drm); + if (ret < 0) { + DRM_ERROR("failed to install DE IRQ handler\n"); + return ret; + } + + /* first enable the DC block IRQs */ + malidp_hw_enable_irq(hwdev, MALIDP_DC_BLOCK, + hwdev->map.dc_irq_map.irq_mask); + + /* now enable the DE block IRQs */ + malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK, + hwdev->map.de_irq_map.irq_mask); + + return 0; +} + +void malidp_de_irq_fini(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + + malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, + hwdev->map.de_irq_map.irq_mask); + malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK, + hwdev->map.dc_irq_map.irq_mask); +} + +static irqreturn_t malidp_se_irq(int irq, void *arg) +{ + struct drm_device *drm = arg; + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + u32 status, mask; + + status = malidp_hw_read(hwdev, hwdev->map.se_base + MALIDP_REG_STATUS); + if (!(status & hwdev->map.se_irq_map.irq_mask)) + return IRQ_NONE; + + mask = malidp_hw_read(hwdev, hwdev->map.se_base + MALIDP_REG_MASKIRQ); + status = malidp_hw_read(hwdev, hwdev->map.se_base + MALIDP_REG_STATUS); + status &= mask; + /* ToDo: status decoding and firing up of VSYNC and page flip events */ + + malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status); + + return IRQ_HANDLED; +} + +static irqreturn_t malidp_se_irq_thread_handler(int irq, void *arg) +{ + return IRQ_HANDLED; +} + +int malidp_se_irq_init(struct drm_device *drm, int irq) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + int ret; + + /* ensure interrupts are disabled */ + malidp_hw_disable_irq(hwdev, MALIDP_SE_BLOCK, 0xffffffff); + malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, 0xffffffff); + + ret = devm_request_threaded_irq(drm->dev, irq, malidp_se_irq, + malidp_se_irq_thread_handler, + IRQF_SHARED, "malidp-se", drm); + if (ret < 0) { + DRM_ERROR("failed to install SE IRQ handler\n"); + return ret; + } + + malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK, + hwdev->map.se_irq_map.irq_mask); + + return 0; +} + +void malidp_se_irq_fini(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; + + malidp_hw_disable_irq(hwdev, MALIDP_SE_BLOCK, + hwdev->map.se_irq_map.irq_mask); +} diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h new file mode 100644 index 000000000000..141743e9f3a6 --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_hw.h @@ -0,0 +1,241 @@ +/* + * + * (C) COPYRIGHT 2013-2016 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP hardware manipulation routines. + */ + +#ifndef __MALIDP_HW_H__ +#define __MALIDP_HW_H__ + +#include <linux/bitops.h> +#include "malidp_regs.h" + +struct videomode; +struct clk; + +/* Mali DP IP blocks */ +enum { + MALIDP_DE_BLOCK = 0, + MALIDP_SE_BLOCK, + MALIDP_DC_BLOCK +}; + +/* Mali DP layer IDs */ +enum { + DE_VIDEO1 = BIT(0), + DE_GRAPHICS1 = BIT(1), + DE_GRAPHICS2 = BIT(2), /* used only in DP500 */ + DE_VIDEO2 = BIT(3), + DE_SMART = BIT(4), +}; + +struct malidp_input_format { + u32 format; /* DRM fourcc */ + u8 layer; /* bitmask of layers supporting it */ + u8 id; /* used internally */ +}; + +#define MALIDP_INVALID_FORMAT_ID 0xff + +/* + * hide the differences between register maps + * by using a common structure to hold the + * base register offsets + */ + +struct malidp_irq_map { + u32 irq_mask; /* mask of IRQs that can be enabled in the block */ + u32 vsync_irq; /* IRQ bit used for signaling during VSYNC */ +}; + +struct malidp_layer { + u16 id; /* layer ID */ + u16 base; /* address offset for the register bank */ + u16 ptr; /* address offset for the pointer register */ +}; + +/* regmap features */ +#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0) + +struct malidp_hw_regmap { + /* address offset of the DE register bank */ + /* is always 0x0000 */ + /* address offset of the SE registers bank */ + const u16 se_base; + /* address offset of the DC registers bank */ + const u16 dc_base; + + /* address offset for the output depth register */ + const u16 out_depth_base; + + /* bitmap with register map features */ + const u8 features; + + /* list of supported layers */ + const u8 n_layers; + const struct malidp_layer *layers; + + const struct malidp_irq_map de_irq_map; + const struct malidp_irq_map se_irq_map; + const struct malidp_irq_map dc_irq_map; + + /* list of supported input formats for each layer */ + const struct malidp_input_format *input_formats; + const u8 n_input_formats; +}; + +struct malidp_hw_device { + const struct malidp_hw_regmap map; + void __iomem *regs; + + /* APB clock */ + struct clk *pclk; + /* AXI clock */ + struct clk *aclk; + /* main clock for display core */ + struct clk *mclk; + /* pixel clock for display core */ + struct clk *pxlclk; + + /* + * Validate the driver instance against the hardware bits + */ + int (*query_hw)(struct malidp_hw_device *hwdev); + + /* + * Set the hardware into config mode, ready to accept mode changes + */ + void (*enter_config_mode)(struct malidp_hw_device *hwdev); + + /* + * Tell hardware to exit configuration mode + */ + void (*leave_config_mode)(struct malidp_hw_device *hwdev); + + /* + * Query if hardware is in configuration mode + */ + bool (*in_config_mode)(struct malidp_hw_device *hwdev); + + /* + * Set configuration valid flag for hardware parameters that can + * be changed outside the configuration mode. Hardware will use + * the new settings when config valid is set after the end of the + * current buffer scanout + */ + void (*set_config_valid)(struct malidp_hw_device *hwdev); + + /* + * Set a new mode in hardware. Requires the hardware to be in + * configuration mode before this function is called. + */ + void (*modeset)(struct malidp_hw_device *hwdev, struct videomode *m); + + /* + * Calculate the required rotation memory given the active area + * and the buffer format. + */ + int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt); + + u8 features; + + u8 min_line_size; + u16 max_line_size; + + /* size of memory used for rotating layers, up to two banks available */ + u32 rotation_memory[2]; +}; + +/* Supported variants of the hardware */ +enum { + MALIDP_500 = 0, + MALIDP_550, + MALIDP_650, + /* keep the next entry last */ + MALIDP_MAX_DEVICES +}; + +extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES]; + +static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg) +{ + return readl(hwdev->regs + reg); +} + +static inline void malidp_hw_write(struct malidp_hw_device *hwdev, + u32 value, u32 reg) +{ + writel(value, hwdev->regs + reg); +} + +static inline void malidp_hw_setbits(struct malidp_hw_device *hwdev, + u32 mask, u32 reg) +{ + u32 data = malidp_hw_read(hwdev, reg); + + data |= mask; + malidp_hw_write(hwdev, data, reg); +} + +static inline void malidp_hw_clearbits(struct malidp_hw_device *hwdev, + u32 mask, u32 reg) +{ + u32 data = malidp_hw_read(hwdev, reg); + + data &= ~mask; + malidp_hw_write(hwdev, data, reg); +} + +static inline u32 malidp_get_block_base(struct malidp_hw_device *hwdev, + u8 block) +{ + switch (block) { + case MALIDP_SE_BLOCK: + return hwdev->map.se_base; + case MALIDP_DC_BLOCK: + return hwdev->map.dc_base; + } + + return 0; +} + +static inline void malidp_hw_disable_irq(struct malidp_hw_device *hwdev, + u8 block, u32 irq) +{ + u32 base = malidp_get_block_base(hwdev, block); + + malidp_hw_clearbits(hwdev, irq, base + MALIDP_REG_MASKIRQ); +} + +static inline void malidp_hw_enable_irq(struct malidp_hw_device *hwdev, + u8 block, u32 irq) +{ + u32 base = malidp_get_block_base(hwdev, block); + + malidp_hw_setbits(hwdev, irq, base + MALIDP_REG_MASKIRQ); +} + +int malidp_de_irq_init(struct drm_device *drm, int irq); +void malidp_de_irq_fini(struct drm_device *drm); +int malidp_se_irq_init(struct drm_device *drm, int irq); +void malidp_se_irq_fini(struct drm_device *drm); + +u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, + u8 layer_id, u32 format); + +/* + * background color components are defined as 12bits values, + * they will be shifted right when stored on hardware that + * supports only 8bits per channel + */ +#define MALIDP_BGND_COLOR_R 0x000 +#define MALIDP_BGND_COLOR_G 0x000 +#define MALIDP_BGND_COLOR_B 0x000 + +#endif /* __MALIDP_HW_H__ */ diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c new file mode 100644 index 000000000000..725098d6179a --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -0,0 +1,298 @@ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Liviu Dudau <Liviu.Dudau@arm.com> + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP plane manipulation routines. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> + +#include "malidp_hw.h" +#include "malidp_drv.h" + +/* Layer specific register offsets */ +#define MALIDP_LAYER_FORMAT 0x000 +#define MALIDP_LAYER_CONTROL 0x004 +#define LAYER_ENABLE (1 << 0) +#define LAYER_ROT_OFFSET 8 +#define LAYER_H_FLIP (1 << 10) +#define LAYER_V_FLIP (1 << 11) +#define LAYER_ROT_MASK (0xf << 8) +#define MALIDP_LAYER_SIZE 0x00c +#define LAYER_H_VAL(x) (((x) & 0x1fff) << 0) +#define LAYER_V_VAL(x) (((x) & 0x1fff) << 16) +#define MALIDP_LAYER_COMP_SIZE 0x010 +#define MALIDP_LAYER_OFFSET 0x014 +#define MALIDP_LAYER_STRIDE 0x018 + +static void malidp_de_plane_destroy(struct drm_plane *plane) +{ + struct malidp_plane *mp = to_malidp_plane(plane); + + if (mp->base.fb) + drm_framebuffer_unreference(mp->base.fb); + + drm_plane_helper_disable(plane); + drm_plane_cleanup(plane); + devm_kfree(plane->dev->dev, mp); +} + +struct drm_plane_state *malidp_duplicate_plane_state(struct drm_plane *plane) +{ + struct malidp_plane_state *state, *m_state; + + if (!plane->state) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) { + m_state = to_malidp_plane_state(plane->state); + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + state->rotmem_size = m_state->rotmem_size; + } + + return &state->base; +} + +void malidp_destroy_plane_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct malidp_plane_state *m_state = to_malidp_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + kfree(m_state); +} + +static const struct drm_plane_funcs malidp_de_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = malidp_de_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = malidp_duplicate_plane_state, + .atomic_destroy_state = malidp_destroy_plane_state, +}; + +static int malidp_de_plane_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct malidp_plane *mp = to_malidp_plane(plane); + struct malidp_plane_state *ms = to_malidp_plane_state(state); + u8 format_id; + u32 src_w, src_h; + + if (!state->crtc || !state->fb) + return 0; + + format_id = malidp_hw_get_format_id(&mp->hwdev->map, mp->layer->id, + state->fb->pixel_format); + if (format_id == MALIDP_INVALID_FORMAT_ID) + return -EINVAL; + + src_w = state->src_w >> 16; + src_h = state->src_h >> 16; + + if ((state->crtc_w > mp->hwdev->max_line_size) || + (state->crtc_h > mp->hwdev->max_line_size) || + (state->crtc_w < mp->hwdev->min_line_size) || + (state->crtc_h < mp->hwdev->min_line_size) || + (state->crtc_w != src_w) || (state->crtc_h != src_h)) + return -EINVAL; + + /* packed RGB888 / BGR888 can't be rotated or flipped */ + if (state->rotation != BIT(DRM_ROTATE_0) && + (state->fb->pixel_format == DRM_FORMAT_RGB888 || + state->fb->pixel_format == DRM_FORMAT_BGR888)) + return -EINVAL; + + ms->rotmem_size = 0; + if (state->rotation & MALIDP_ROTATED_MASK) { + int val; + + val = mp->hwdev->rotmem_required(mp->hwdev, state->crtc_h, + state->crtc_w, + state->fb->pixel_format); + if (val < 0) + return val; + + ms->rotmem_size = val; + } + + return 0; +} + +static void malidp_de_plane_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_gem_cma_object *obj; + struct malidp_plane *mp; + const struct malidp_hw_regmap *map; + u8 format_id; + u16 ptr; + u32 format, src_w, src_h, dest_w, dest_h, val = 0; + int num_planes, i; + + mp = to_malidp_plane(plane); + + map = &mp->hwdev->map; + format = plane->state->fb->pixel_format; + format_id = malidp_hw_get_format_id(map, mp->layer->id, format); + num_planes = drm_format_num_planes(format); + + /* convert src values from Q16 fixed point to integer */ + src_w = plane->state->src_w >> 16; + src_h = plane->state->src_h >> 16; + if (plane->state->rotation & MALIDP_ROTATED_MASK) { + dest_w = plane->state->crtc_h; + dest_h = plane->state->crtc_w; + } else { + dest_w = plane->state->crtc_w; + dest_h = plane->state->crtc_h; + } + + malidp_hw_write(mp->hwdev, format_id, mp->layer->base); + + for (i = 0; i < num_planes; i++) { + /* calculate the offset for the layer's plane registers */ + ptr = mp->layer->ptr + (i << 4); + + obj = drm_fb_cma_get_gem_obj(plane->state->fb, i); + malidp_hw_write(mp->hwdev, lower_32_bits(obj->paddr), ptr); + malidp_hw_write(mp->hwdev, upper_32_bits(obj->paddr), ptr + 4); + malidp_hw_write(mp->hwdev, plane->state->fb->pitches[i], + mp->layer->base + MALIDP_LAYER_STRIDE); + } + + malidp_hw_write(mp->hwdev, LAYER_H_VAL(src_w) | LAYER_V_VAL(src_h), + mp->layer->base + MALIDP_LAYER_SIZE); + + malidp_hw_write(mp->hwdev, LAYER_H_VAL(dest_w) | LAYER_V_VAL(dest_h), + mp->layer->base + MALIDP_LAYER_COMP_SIZE); + + malidp_hw_write(mp->hwdev, LAYER_H_VAL(plane->state->crtc_x) | + LAYER_V_VAL(plane->state->crtc_y), + mp->layer->base + MALIDP_LAYER_OFFSET); + + /* first clear the rotation bits in the register */ + malidp_hw_clearbits(mp->hwdev, LAYER_ROT_MASK, + mp->layer->base + MALIDP_LAYER_CONTROL); + + /* setup the rotation and axis flip bits */ + if (plane->state->rotation & DRM_ROTATE_MASK) + val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET; + if (plane->state->rotation & BIT(DRM_REFLECT_X)) + val |= LAYER_V_FLIP; + if (plane->state->rotation & BIT(DRM_REFLECT_Y)) + val |= LAYER_H_FLIP; + + /* set the 'enable layer' bit */ + val |= LAYER_ENABLE; + + malidp_hw_setbits(mp->hwdev, val, + mp->layer->base + MALIDP_LAYER_CONTROL); +} + +static void malidp_de_plane_disable(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct malidp_plane *mp = to_malidp_plane(plane); + + malidp_hw_clearbits(mp->hwdev, LAYER_ENABLE, + mp->layer->base + MALIDP_LAYER_CONTROL); +} + +static const struct drm_plane_helper_funcs malidp_de_plane_helper_funcs = { + .atomic_check = malidp_de_plane_check, + .atomic_update = malidp_de_plane_update, + .atomic_disable = malidp_de_plane_disable, +}; + +int malidp_de_planes_init(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + const struct malidp_hw_regmap *map = &malidp->dev->map; + struct malidp_plane *plane = NULL; + enum drm_plane_type plane_type; + unsigned long crtcs = 1 << drm->mode_config.num_crtc; + u32 *formats; + int ret, i, j, n; + + formats = kcalloc(map->n_input_formats, sizeof(*formats), GFP_KERNEL); + if (!formats) { + ret = -ENOMEM; + goto cleanup; + } + + for (i = 0; i < map->n_layers; i++) { + u8 id = map->layers[i].id; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) { + ret = -ENOMEM; + goto cleanup; + } + + /* build the list of DRM supported formats based on the map */ + for (n = 0, j = 0; j < map->n_input_formats; j++) { + if ((map->input_formats[j].layer & id) == id) + formats[n++] = map->input_formats[j].format; + } + + plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY : + DRM_PLANE_TYPE_OVERLAY; + ret = drm_universal_plane_init(drm, &plane->base, crtcs, + &malidp_de_plane_funcs, formats, + n, plane_type, NULL); + if (ret < 0) + goto cleanup; + + if (!drm->mode_config.rotation_property) { + unsigned long flags = BIT(DRM_ROTATE_0) | + BIT(DRM_ROTATE_90) | + BIT(DRM_ROTATE_180) | + BIT(DRM_ROTATE_270) | + BIT(DRM_REFLECT_X) | + BIT(DRM_REFLECT_Y); + drm->mode_config.rotation_property = + drm_mode_create_rotation_property(drm, flags); + } + /* SMART layer can't be rotated */ + if (drm->mode_config.rotation_property && (id != DE_SMART)) + drm_object_attach_property(&plane->base.base, + drm->mode_config.rotation_property, + BIT(DRM_ROTATE_0)); + + drm_plane_helper_add(&plane->base, + &malidp_de_plane_helper_funcs); + plane->hwdev = malidp->dev; + plane->layer = &map->layers[i]; + } + + kfree(formats); + + return 0; + +cleanup: + malidp_de_planes_destroy(drm); + kfree(formats); + + return ret; +} + +void malidp_de_planes_destroy(struct drm_device *drm) +{ + struct drm_plane *p, *pt; + + list_for_each_entry_safe(p, pt, &drm->mode_config.plane_list, head) { + drm_plane_cleanup(p); + kfree(p); + } +} diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h new file mode 100644 index 000000000000..73fecb38f955 --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_regs.h @@ -0,0 +1,172 @@ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Liviu Dudau <Liviu.Dudau@arm.com> + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * ARM Mali DP500/DP550/DP650 registers definition. + */ + +#ifndef __MALIDP_REGS_H__ +#define __MALIDP_REGS_H__ + +/* + * abbreviations used: + * - DC - display core (general settings) + * - DE - display engine + * - SE - scaling engine + */ + +/* interrupt bit masks */ +#define MALIDP_DE_IRQ_UNDERRUN (1 << 0) + +#define MALIDP500_DE_IRQ_AXI_ERR (1 << 4) +#define MALIDP500_DE_IRQ_VSYNC (1 << 5) +#define MALIDP500_DE_IRQ_PROG_LINE (1 << 6) +#define MALIDP500_DE_IRQ_SATURATION (1 << 7) +#define MALIDP500_DE_IRQ_CONF_VALID (1 << 8) +#define MALIDP500_DE_IRQ_CONF_MODE (1 << 11) +#define MALIDP500_DE_IRQ_CONF_ACTIVE (1 << 17) +#define MALIDP500_DE_IRQ_PM_ACTIVE (1 << 18) +#define MALIDP500_DE_IRQ_TESTMODE_ACTIVE (1 << 19) +#define MALIDP500_DE_IRQ_FORCE_BLNK_ACTIVE (1 << 24) +#define MALIDP500_DE_IRQ_AXI_BUSY (1 << 28) +#define MALIDP500_DE_IRQ_GLOBAL (1 << 31) +#define MALIDP500_SE_IRQ_CONF_MODE (1 << 0) +#define MALIDP500_SE_IRQ_CONF_VALID (1 << 4) +#define MALIDP500_SE_IRQ_INIT_BUSY (1 << 5) +#define MALIDP500_SE_IRQ_AXI_ERROR (1 << 8) +#define MALIDP500_SE_IRQ_OVERRUN (1 << 9) +#define MALIDP500_SE_IRQ_PROG_LINE1 (1 << 12) +#define MALIDP500_SE_IRQ_PROG_LINE2 (1 << 13) +#define MALIDP500_SE_IRQ_CONF_ACTIVE (1 << 17) +#define MALIDP500_SE_IRQ_PM_ACTIVE (1 << 18) +#define MALIDP500_SE_IRQ_AXI_BUSY (1 << 28) +#define MALIDP500_SE_IRQ_GLOBAL (1 << 31) + +#define MALIDP550_DE_IRQ_SATURATION (1 << 8) +#define MALIDP550_DE_IRQ_VSYNC (1 << 12) +#define MALIDP550_DE_IRQ_PROG_LINE (1 << 13) +#define MALIDP550_DE_IRQ_AXI_ERR (1 << 16) +#define MALIDP550_SE_IRQ_EOW (1 << 0) +#define MALIDP550_SE_IRQ_AXI_ERR (1 << 16) +#define MALIDP550_DC_IRQ_CONF_VALID (1 << 0) +#define MALIDP550_DC_IRQ_CONF_MODE (1 << 4) +#define MALIDP550_DC_IRQ_CONF_ACTIVE (1 << 16) +#define MALIDP550_DC_IRQ_DE (1 << 20) +#define MALIDP550_DC_IRQ_SE (1 << 24) + +#define MALIDP650_DE_IRQ_DRIFT (1 << 4) + +/* bit masks that are common between products */ +#define MALIDP_CFG_VALID (1 << 0) +#define MALIDP_DISP_FUNC_ILACED (1 << 8) + +/* register offsets for IRQ management */ +#define MALIDP_REG_STATUS 0x00000 +#define MALIDP_REG_SETIRQ 0x00004 +#define MALIDP_REG_MASKIRQ 0x00008 +#define MALIDP_REG_CLEARIRQ 0x0000c + +/* register offsets */ +#define MALIDP_DE_CORE_ID 0x00018 +#define MALIDP_DE_DISPLAY_FUNC 0x00020 + +/* these offsets are relative to MALIDP5x0_TIMINGS_BASE */ +#define MALIDP_DE_H_TIMINGS 0x0 +#define MALIDP_DE_V_TIMINGS 0x4 +#define MALIDP_DE_SYNC_WIDTH 0x8 +#define MALIDP_DE_HV_ACTIVE 0xc + +/* macros to set values into registers */ +#define MALIDP_DE_H_FRONTPORCH(x) (((x) & 0xfff) << 0) +#define MALIDP_DE_H_BACKPORCH(x) (((x) & 0x3ff) << 16) +#define MALIDP500_DE_V_FRONTPORCH(x) (((x) & 0xff) << 0) +#define MALIDP550_DE_V_FRONTPORCH(x) (((x) & 0xfff) << 0) +#define MALIDP_DE_V_BACKPORCH(x) (((x) & 0xff) << 16) +#define MALIDP_DE_H_SYNCWIDTH(x) (((x) & 0x3ff) << 0) +#define MALIDP_DE_V_SYNCWIDTH(x) (((x) & 0xff) << 16) +#define MALIDP_DE_H_ACTIVE(x) (((x) & 0x1fff) << 0) +#define MALIDP_DE_V_ACTIVE(x) (((x) & 0x1fff) << 16) + +/* register offsets and bits specific to DP500 */ +#define MALIDP500_DC_BASE 0x00000 +#define MALIDP500_DC_CONTROL 0x0000c +#define MALIDP500_DC_CONFIG_REQ (1 << 17) +#define MALIDP500_HSYNCPOL (1 << 20) +#define MALIDP500_VSYNCPOL (1 << 21) +#define MALIDP500_DC_CLEAR_MASK 0x300fff +#define MALIDP500_DE_LINE_COUNTER 0x00010 +#define MALIDP500_DE_AXI_CONTROL 0x00014 +#define MALIDP500_DE_SECURE_CTRL 0x0001c +#define MALIDP500_DE_CHROMA_KEY 0x00024 +#define MALIDP500_TIMINGS_BASE 0x00028 + +#define MALIDP500_CONFIG_3D 0x00038 +#define MALIDP500_BGND_COLOR 0x0003c +#define MALIDP500_OUTPUT_DEPTH 0x00044 +#define MALIDP500_YUV_RGB_COEF 0x00048 +#define MALIDP500_COLOR_ADJ_COEF 0x00078 +#define MALIDP500_COEF_TABLE_ADDR 0x000a8 +#define MALIDP500_COEF_TABLE_DATA 0x000ac +#define MALIDP500_DE_LV_BASE 0x00100 +#define MALIDP500_DE_LV_PTR_BASE 0x00124 +#define MALIDP500_DE_LG1_BASE 0x00200 +#define MALIDP500_DE_LG1_PTR_BASE 0x0021c +#define MALIDP500_DE_LG2_BASE 0x00300 +#define MALIDP500_DE_LG2_PTR_BASE 0x0031c +#define MALIDP500_SE_BASE 0x00c00 +#define MALIDP500_SE_PTR_BASE 0x00e0c +#define MALIDP500_DC_IRQ_BASE 0x00f00 +#define MALIDP500_CONFIG_VALID 0x00f00 +#define MALIDP500_CONFIG_ID 0x00fd4 + +/* register offsets and bits specific to DP550/DP650 */ +#define MALIDP550_DE_CONTROL 0x00010 +#define MALIDP550_DE_LINE_COUNTER 0x00014 +#define MALIDP550_DE_AXI_CONTROL 0x00018 +#define MALIDP550_DE_QOS 0x0001c +#define MALIDP550_TIMINGS_BASE 0x00030 +#define MALIDP550_HSYNCPOL (1 << 12) +#define MALIDP550_VSYNCPOL (1 << 28) + +#define MALIDP550_DE_DISP_SIDEBAND 0x00040 +#define MALIDP550_DE_BGND_COLOR 0x00044 +#define MALIDP550_DE_OUTPUT_DEPTH 0x0004c +#define MALIDP550_DE_COLOR_COEF 0x00050 +#define MALIDP550_DE_COEF_TABLE_ADDR 0x00080 +#define MALIDP550_DE_COEF_TABLE_DATA 0x00084 +#define MALIDP550_DE_LV1_BASE 0x00100 +#define MALIDP550_DE_LV1_PTR_BASE 0x00124 +#define MALIDP550_DE_LV2_BASE 0x00200 +#define MALIDP550_DE_LV2_PTR_BASE 0x00224 +#define MALIDP550_DE_LG_BASE 0x00300 +#define MALIDP550_DE_LG_PTR_BASE 0x0031c +#define MALIDP550_DE_LS_BASE 0x00400 +#define MALIDP550_DE_LS_PTR_BASE 0x0042c +#define MALIDP550_DE_PERF_BASE 0x00500 +#define MALIDP550_SE_BASE 0x08000 +#define MALIDP550_DC_BASE 0x0c000 +#define MALIDP550_DC_CONTROL 0x0c010 +#define MALIDP550_DC_CONFIG_REQ (1 << 16) +#define MALIDP550_CONFIG_VALID 0x0c014 +#define MALIDP550_CONFIG_ID 0x0ffd4 + +/* + * Starting with DP550 the register map blocks has been standardised to the + * following layout: + * + * Offset Block registers + * 0x00000 Display Engine + * 0x08000 Scaling Engine + * 0x0c000 Display Core + * 0x10000 Secure control + * + * The old DP500 IP mixes some DC with the DE registers, hence the need + * for a mapping structure. + */ + +#endif /* __MALIDP_REGS_H__ */ diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index cb21c0b6374a..f5ebdd681445 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -189,7 +189,6 @@ static struct drm_driver armada_drm_driver = { .load = armada_drm_load, .lastclose = armada_drm_lastclose, .unload = armada_drm_unload, - .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = armada_drm_enable_vblank, .disable_vblank = armada_drm_disable_vblank, diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 148e8a42b2c6..1ee707ef6b8d 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -121,6 +121,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, int ret; ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, + BIT(DRM_ROTATE_0), 0, INT_MAX, true, false, &visible); if (ret) return ret; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 9ecf16c7911d..d4a3d61b7b06 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -691,13 +691,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) destroy_workqueue(dc->wq); } -static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) -{ - mutex_lock(&dev->mode_config.mutex); - drm_connector_unregister_all(dev); - mutex_unlock(&dev->mode_config.mutex); -} - static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) { struct atmel_hlcdc_dc *dc = dev->dev_private; @@ -815,15 +808,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) if (ret) goto err_unload; - ret = drm_connector_register_all(ddev); - if (ret) - goto err_unregister; - return 0; -err_unregister: - drm_dev_unregister(ddev); - err_unload: atmel_hlcdc_dc_unload(ddev); @@ -837,7 +823,6 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) { struct drm_device *ddev = platform_get_drvdata(pdev); - atmel_hlcdc_dc_connector_unplug_all(ddev); drm_dev_unregister(ddev); atmel_hlcdc_dc_unload(ddev); drm_dev_unref(ddev); diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 8f7423f18da5..a141921445f4 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -50,6 +50,14 @@ config DRM_PARADE_PS8622 ---help--- Parade eDP-LVDS bridge chip driver. +config DRM_SII902X + tristate "Silicon Image sii902x RGB/HDMI bridge" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + ---help--- + Silicon Image sii902x bridge chip driver. + source "drivers/gpu/drm/bridge/analogix/Kconfig" endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 96b13b30e6ab..bfec9f8cb9d2 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o +obj-$(CONFIG_DRM_SII902X) += sii902x.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c new file mode 100644 index 000000000000..9126d0306ab5 --- /dev/null +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2016 Atmel + * Bo Shen <voice.shen@atmel.com> + * + * Authors: Bo Shen <voice.shen@atmel.com> + * Boris Brezillon <boris.brezillon@free-electrons.com> + * Wu, Songjun <Songjun.Wu@atmel.com> + * + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> + +#define SII902X_TPI_VIDEO_DATA 0x0 + +#define SII902X_TPI_PIXEL_REPETITION 0x8 +#define SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT BIT(5) +#define SII902X_TPI_AVI_PIXEL_REP_RISING_EDGE BIT(4) +#define SII902X_TPI_AVI_PIXEL_REP_4X 3 +#define SII902X_TPI_AVI_PIXEL_REP_2X 1 +#define SII902X_TPI_AVI_PIXEL_REP_NONE 0 +#define SII902X_TPI_CLK_RATIO_HALF (0 << 6) +#define SII902X_TPI_CLK_RATIO_1X (1 << 6) +#define SII902X_TPI_CLK_RATIO_2X (2 << 6) +#define SII902X_TPI_CLK_RATIO_4X (3 << 6) + +#define SII902X_TPI_AVI_IN_FORMAT 0x9 +#define SII902X_TPI_AVI_INPUT_BITMODE_12BIT BIT(7) +#define SII902X_TPI_AVI_INPUT_DITHER BIT(6) +#define SII902X_TPI_AVI_INPUT_RANGE_LIMITED (2 << 2) +#define SII902X_TPI_AVI_INPUT_RANGE_FULL (1 << 2) +#define SII902X_TPI_AVI_INPUT_RANGE_AUTO (0 << 2) +#define SII902X_TPI_AVI_INPUT_COLORSPACE_BLACK (3 << 0) +#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422 (2 << 0) +#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444 (1 << 0) +#define SII902X_TPI_AVI_INPUT_COLORSPACE_RGB (0 << 0) + +#define SII902X_TPI_AVI_INFOFRAME 0x0c + +#define SII902X_SYS_CTRL_DATA 0x1a +#define SII902X_SYS_CTRL_PWR_DWN BIT(4) +#define SII902X_SYS_CTRL_AV_MUTE BIT(3) +#define SII902X_SYS_CTRL_DDC_BUS_REQ BIT(2) +#define SII902X_SYS_CTRL_DDC_BUS_GRTD BIT(1) +#define SII902X_SYS_CTRL_OUTPUT_MODE BIT(0) +#define SII902X_SYS_CTRL_OUTPUT_HDMI 1 +#define SII902X_SYS_CTRL_OUTPUT_DVI 0 + +#define SII902X_REG_CHIPID(n) (0x1b + (n)) + +#define SII902X_PWR_STATE_CTRL 0x1e +#define SII902X_AVI_POWER_STATE_MSK GENMASK(1, 0) +#define SII902X_AVI_POWER_STATE_D(l) ((l) & SII902X_AVI_POWER_STATE_MSK) + +#define SII902X_INT_ENABLE 0x3c +#define SII902X_INT_STATUS 0x3d +#define SII902X_HOTPLUG_EVENT BIT(0) +#define SII902X_PLUGGED_STATUS BIT(2) + +#define SII902X_REG_TPI_RQB 0xc7 + +#define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS 500 + +struct sii902x { + struct i2c_client *i2c; + struct regmap *regmap; + struct drm_bridge bridge; + struct drm_connector connector; + struct gpio_desc *reset_gpio; +}; + +static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge) +{ + return container_of(bridge, struct sii902x, bridge); +} + +static inline struct sii902x *connector_to_sii902x(struct drm_connector *con) +{ + return container_of(con, struct sii902x, connector); +} + +static void sii902x_reset(struct sii902x *sii902x) +{ + if (!sii902x->reset_gpio) + return; + + gpiod_set_value(sii902x->reset_gpio, 1); + + /* The datasheet says treset-min = 100us. Make it 150us to be sure. */ + usleep_range(150, 200); + + gpiod_set_value(sii902x->reset_gpio, 0); +} + +static enum drm_connector_status +sii902x_connector_detect(struct drm_connector *connector, bool force) +{ + struct sii902x *sii902x = connector_to_sii902x(connector); + unsigned int status; + + regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); + + return (status & SII902X_PLUGGED_STATUS) ? + connector_status_connected : connector_status_disconnected; +} + +static const struct drm_connector_funcs sii902x_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = sii902x_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int sii902x_get_modes(struct drm_connector *connector) +{ + struct sii902x *sii902x = connector_to_sii902x(connector); + struct regmap *regmap = sii902x->regmap; + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; + unsigned long timeout; + unsigned int status; + struct edid *edid; + int num = 0; + int ret; + + ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_DDC_BUS_REQ, + SII902X_SYS_CTRL_DDC_BUS_REQ); + if (ret) + return ret; + + timeout = jiffies + + msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); + do { + ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); + if (ret) + return ret; + } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && + time_before(jiffies, timeout)); + + if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { + dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus"); + return -ETIMEDOUT; + } + + ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status); + if (ret) + return ret; + + edid = drm_get_edid(connector, sii902x->i2c->adapter); + drm_mode_connector_update_edid_property(connector, edid); + if (edid) { + num = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + ret = drm_display_info_set_bus_formats(&connector->display_info, + &bus_format, 1); + if (ret) + return ret; + + ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_DDC_BUS_REQ | + SII902X_SYS_CTRL_DDC_BUS_GRTD, 0); + if (ret) + return ret; + + timeout = jiffies + + msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); + do { + ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); + if (ret) + return ret; + } while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | + SII902X_SYS_CTRL_DDC_BUS_GRTD) && + time_before(jiffies, timeout)); + + if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | + SII902X_SYS_CTRL_DDC_BUS_GRTD)) { + dev_err(&sii902x->i2c->dev, "failed to release the i2c bus"); + return -ETIMEDOUT; + } + + return num; +} + +static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* TODO: check mode */ + + return MODE_OK; +} + +static const struct drm_connector_helper_funcs sii902x_connector_helper_funcs = { + .get_modes = sii902x_get_modes, + .mode_valid = sii902x_mode_valid, +}; + +static void sii902x_bridge_disable(struct drm_bridge *bridge) +{ + struct sii902x *sii902x = bridge_to_sii902x(bridge); + + regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_PWR_DWN, + SII902X_SYS_CTRL_PWR_DWN); +} + +static void sii902x_bridge_enable(struct drm_bridge *bridge) +{ + struct sii902x *sii902x = bridge_to_sii902x(bridge); + + regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL, + SII902X_AVI_POWER_STATE_MSK, + SII902X_AVI_POWER_STATE_D(0)); + regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_PWR_DWN, 0); +} + +static void sii902x_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj) +{ + struct sii902x *sii902x = bridge_to_sii902x(bridge); + struct regmap *regmap = sii902x->regmap; + u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; + struct hdmi_avi_infoframe frame; + int ret; + + buf[0] = adj->clock; + buf[1] = adj->clock >> 8; + buf[2] = adj->vrefresh; + buf[3] = 0x00; + buf[4] = adj->hdisplay; + buf[5] = adj->hdisplay >> 8; + buf[6] = adj->vdisplay; + buf[7] = adj->vdisplay >> 8; + buf[8] = SII902X_TPI_CLK_RATIO_1X | SII902X_TPI_AVI_PIXEL_REP_NONE | + SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT; + buf[9] = SII902X_TPI_AVI_INPUT_RANGE_AUTO | + SII902X_TPI_AVI_INPUT_COLORSPACE_RGB; + + ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10); + if (ret) + return; + + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, adj); + if (ret < 0) { + DRM_ERROR("couldn't fill AVI infoframe\n"); + return; + } + + ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); + if (ret < 0) { + DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); + return; + } + + /* Do not send the infoframe header, but keep the CRC field. */ + regmap_bulk_write(regmap, SII902X_TPI_AVI_INFOFRAME, + buf + HDMI_INFOFRAME_HEADER_SIZE - 1, + HDMI_AVI_INFOFRAME_SIZE + 1); +} + +static int sii902x_bridge_attach(struct drm_bridge *bridge) +{ + struct sii902x *sii902x = bridge_to_sii902x(bridge); + struct drm_device *drm = bridge->dev; + int ret; + + drm_connector_helper_add(&sii902x->connector, + &sii902x_connector_helper_funcs); + + if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) { + dev_err(&sii902x->i2c->dev, + "sii902x driver is only compatible with DRM devices supporting atomic updates"); + return -ENOTSUPP; + } + + ret = drm_connector_init(drm, &sii902x->connector, + &sii902x_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + if (ret) + return ret; + + if (sii902x->i2c->irq > 0) + sii902x->connector.polled = DRM_CONNECTOR_POLL_HPD; + else + sii902x->connector.polled = DRM_CONNECTOR_POLL_CONNECT; + + drm_mode_connector_attach_encoder(&sii902x->connector, bridge->encoder); + + return 0; +} + +static const struct drm_bridge_funcs sii902x_bridge_funcs = { + .attach = sii902x_bridge_attach, + .mode_set = sii902x_bridge_mode_set, + .disable = sii902x_bridge_disable, + .enable = sii902x_bridge_enable, +}; + +static const struct regmap_range sii902x_volatile_ranges[] = { + { .range_min = 0, .range_max = 0xff }, +}; + +static const struct regmap_access_table sii902x_volatile_table = { + .yes_ranges = sii902x_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(sii902x_volatile_ranges), +}; + +static const struct regmap_config sii902x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &sii902x_volatile_table, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t sii902x_interrupt(int irq, void *data) +{ + struct sii902x *sii902x = data; + unsigned int status = 0; + + regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); + regmap_write(sii902x->regmap, SII902X_INT_STATUS, status); + + if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev) + drm_helper_hpd_irq_event(sii902x->bridge.dev); + + return IRQ_HANDLED; +} + +static int sii902x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + unsigned int status = 0; + struct sii902x *sii902x; + u8 chipid[4]; + int ret; + + sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL); + if (!sii902x) + return -ENOMEM; + + sii902x->i2c = client; + sii902x->regmap = devm_regmap_init_i2c(client, &sii902x_regmap_config); + if (IS_ERR(sii902x->regmap)) + return PTR_ERR(sii902x->regmap); + + sii902x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(sii902x->reset_gpio)) { + dev_err(dev, "Failed to retrieve/request reset gpio: %ld\n", + PTR_ERR(sii902x->reset_gpio)); + return PTR_ERR(sii902x->reset_gpio); + } + + sii902x_reset(sii902x); + + ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0); + if (ret) + return ret; + + ret = regmap_bulk_read(sii902x->regmap, SII902X_REG_CHIPID(0), + &chipid, 4); + if (ret) { + dev_err(dev, "regmap_read failed %d\n", ret); + return ret; + } + + if (chipid[0] != 0xb0) { + dev_err(dev, "Invalid chipid: %02x (expecting 0xb0)\n", + chipid[0]); + return -EINVAL; + } + + /* Clear all pending interrupts */ + regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); + regmap_write(sii902x->regmap, SII902X_INT_STATUS, status); + + if (client->irq > 0) { + regmap_write(sii902x->regmap, SII902X_INT_ENABLE, + SII902X_HOTPLUG_EVENT); + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + sii902x_interrupt, + IRQF_ONESHOT, dev_name(dev), + sii902x); + if (ret) + return ret; + } + + sii902x->bridge.funcs = &sii902x_bridge_funcs; + sii902x->bridge.of_node = dev->of_node; + ret = drm_bridge_add(&sii902x->bridge); + if (ret) { + dev_err(dev, "Failed to add drm_bridge\n"); + return ret; + } + + i2c_set_clientdata(client, sii902x); + + return 0; +} + +static int sii902x_remove(struct i2c_client *client) + +{ + struct sii902x *sii902x = i2c_get_clientdata(client); + + drm_bridge_remove(&sii902x->bridge); + + return 0; +} + +static const struct of_device_id sii902x_dt_ids[] = { + { .compatible = "sil,sii9022", }, + { } +}; +MODULE_DEVICE_TABLE(of, sii902x_dt_ids); + +static const struct i2c_device_id sii902x_i2c_ids[] = { + { "sii9022", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids); + +static struct i2c_driver sii902x_driver = { + .probe = sii902x_probe, + .remove = sii902x_remove, + .driver = { + .name = "sii902x", + .of_match_table = sii902x_dt_ids, + }, + .id_table = sii902x_i2c_ids, +}; +module_i2c_driver(sii902x_driver); + +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); +MODULE_DESCRIPTION("SII902x RGB -> HDMI bridges"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 50d0baa06db0..4153e8a193af 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -30,25 +30,36 @@ #include <drm/drmP.h> #include "drm_internal.h" +#include "drm_legacy.h" /** - * drm_getmagic - Get unique magic of a client - * @dev: DRM device to operate on - * @data: ioctl data containing the drm_auth object - * @file_priv: DRM file that performs the operation + * DOC: master and authentication * - * This looks up the unique magic of the passed client and returns it. If the - * client did not have a magic assigned, yet, a new one is registered. The magic - * is stored in the passed drm_auth object. + * struct &drm_master is used to track groups of clients with open + * primary/legacy device nodes. For every struct &drm_file which has had at + * least once successfully became the device master (either through the + * SET_MASTER IOCTL, or implicitly through opening the primary device node when + * no one else is the current master that time) there exists one &drm_master. + * This is noted in the is_master member of &drm_file. All other clients have + * just a pointer to the &drm_master they are associated with. * - * Returns: 0 on success, negative error code on failure. + * In addition only one &drm_master can be the current master for a &drm_device. + * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or + * implicitly through closing/openeing the primary device node. See also + * drm_is_current_master(). + * + * Clients can authenticate against the current master (if it matches their own) + * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters, + * this allows controlled access to the device for an entire group of mutually + * trusted clients. */ + int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_auth *auth = data; int ret = 0; - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev->master_mutex); if (!file_priv->magic) { ret = idr_alloc(&file_priv->master->magic_map, file_priv, 1, 0, GFP_KERNEL); @@ -56,23 +67,13 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) file_priv->magic = ret; } auth->magic = file_priv->magic; - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->master_mutex); DRM_DEBUG("%u\n", auth->magic); return ret < 0 ? ret : 0; } -/** - * drm_authmagic - Authenticate client with a magic - * @dev: DRM device to operate on - * @data: ioctl data containing the drm_auth object - * @file_priv: DRM file that performs the operation - * - * This looks up a DRM client by the passed magic and authenticates it. - * - * Returns: 0 on success, negative error code on failure. - */ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -81,13 +82,253 @@ int drm_authmagic(struct drm_device *dev, void *data, DRM_DEBUG("%u\n", auth->magic); - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev->master_mutex); file = idr_find(&file_priv->master->magic_map, auth->magic); if (file) { file->authenticated = 1; idr_replace(&file_priv->master->magic_map, NULL, auth->magic); } - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->master_mutex); return file ? 0 : -EINVAL; } + +static struct drm_master *drm_master_create(struct drm_device *dev) +{ + struct drm_master *master; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return NULL; + + kref_init(&master->refcount); + spin_lock_init(&master->lock.spinlock); + init_waitqueue_head(&master->lock.lock_queue); + idr_init(&master->magic_map); + master->dev = dev; + + return master; +} + +static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv, + bool new_master) +{ + int ret = 0; + + dev->master = drm_master_get(fpriv->master); + if (dev->driver->master_set) { + ret = dev->driver->master_set(dev, fpriv, new_master); + if (unlikely(ret != 0)) { + drm_master_put(&dev->master); + } + } + + return ret; +} + +static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) +{ + struct drm_master *old_master; + int ret; + + lockdep_assert_held_once(&dev->master_mutex); + + old_master = fpriv->master; + fpriv->master = drm_master_create(dev); + if (!fpriv->master) { + fpriv->master = old_master; + return -ENOMEM; + } + + if (dev->driver->master_create) { + ret = dev->driver->master_create(dev, fpriv->master); + if (ret) + goto out_err; + } + fpriv->is_master = 1; + fpriv->authenticated = 1; + + ret = drm_set_master(dev, fpriv, true); + if (ret) + goto out_err; + + if (old_master) + drm_master_put(&old_master); + + return 0; + +out_err: + /* drop references and restore old master on failure */ + drm_master_put(&fpriv->master); + fpriv->master = old_master; + + return ret; +} + +int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + int ret = 0; + + mutex_lock(&dev->master_mutex); + if (drm_is_current_master(file_priv)) + goto out_unlock; + + if (dev->master) { + ret = -EINVAL; + goto out_unlock; + } + + if (!file_priv->master) { + ret = -EINVAL; + goto out_unlock; + } + + if (!file_priv->is_master) { + ret = drm_new_set_master(dev, file_priv); + goto out_unlock; + } + + ret = drm_set_master(dev, file_priv, false); +out_unlock: + mutex_unlock(&dev->master_mutex); + return ret; +} + +static void drm_drop_master(struct drm_device *dev, + struct drm_file *fpriv) +{ + if (dev->driver->master_drop) + dev->driver->master_drop(dev, fpriv); + drm_master_put(&dev->master); +} + +int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + int ret = -EINVAL; + + mutex_lock(&dev->master_mutex); + if (!drm_is_current_master(file_priv)) + goto out_unlock; + + if (!dev->master) + goto out_unlock; + + ret = 0; + drm_drop_master(dev, file_priv); +out_unlock: + mutex_unlock(&dev->master_mutex); + return ret; +} + +int drm_master_open(struct drm_file *file_priv) +{ + struct drm_device *dev = file_priv->minor->dev; + int ret = 0; + + /* if there is no current master make this fd it, but do not create + * any master object for render clients */ + mutex_lock(&dev->master_mutex); + if (!dev->master) + ret = drm_new_set_master(dev, file_priv); + else + file_priv->master = drm_master_get(dev->master); + mutex_unlock(&dev->master_mutex); + + return ret; +} + +void drm_master_release(struct drm_file *file_priv) +{ + struct drm_device *dev = file_priv->minor->dev; + struct drm_master *master = file_priv->master; + + mutex_lock(&dev->master_mutex); + if (file_priv->magic) + idr_remove(&file_priv->master->magic_map, file_priv->magic); + + if (!drm_is_current_master(file_priv)) + goto out; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) { + /* + * Since the master is disappearing, so is the + * possibility to lock. + */ + mutex_lock(&dev->struct_mutex); + if (master->lock.hw_lock) { + if (dev->sigdata.lock == master->lock.hw_lock) + dev->sigdata.lock = NULL; + master->lock.hw_lock = NULL; + master->lock.file_priv = NULL; + wake_up_interruptible_all(&master->lock.lock_queue); + } + mutex_unlock(&dev->struct_mutex); + } + + if (dev->master == file_priv->master) + drm_drop_master(dev, file_priv); +out: + /* drop the master reference held by the file priv */ + if (file_priv->master) + drm_master_put(&file_priv->master); + mutex_unlock(&dev->master_mutex); +} + +/** + * drm_is_current_master - checks whether @priv is the current master + * @fpriv: DRM file private + * + * Checks whether @fpriv is current master on its device. This decides whether a + * client is allowed to run DRM_MASTER IOCTLs. + * + * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting + * - the current master is assumed to own the non-shareable display hardware. + */ +bool drm_is_current_master(struct drm_file *fpriv) +{ + return fpriv->is_master && fpriv->master == fpriv->minor->dev->master; +} +EXPORT_SYMBOL(drm_is_current_master); + +/** + * drm_master_get - reference a master pointer + * @master: struct &drm_master + * + * Increments the reference count of @master and returns a pointer to @master. + */ +struct drm_master *drm_master_get(struct drm_master *master) +{ + kref_get(&master->refcount); + return master; +} +EXPORT_SYMBOL(drm_master_get); + +static void drm_master_destroy(struct kref *kref) +{ + struct drm_master *master = container_of(kref, struct drm_master, refcount); + struct drm_device *dev = master->dev; + + if (dev->driver->master_destroy) + dev->driver->master_destroy(dev, master); + + drm_legacy_master_rmmaps(dev, master); + + idr_destroy(&master->magic_map); + kfree(master->unique); + kfree(master); +} + +/** + * drm_master_put - unreference and clear a master pointer + * @master: pointer to a pointer of struct &drm_master + * + * This decrements the &drm_master behind @master and sets it to NULL. + */ +void drm_master_put(struct drm_master **master) +{ + kref_put(&(*master)->refcount, drm_master_destroy); + *master = NULL; +} +EXPORT_SYMBOL(drm_master_put); diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 9b34158c0f77..c3a12cd8bd0d 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -51,7 +51,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, */ if (!entry->map || map->type != entry->map->type || - entry->master != dev->primary->master) + entry->master != dev->master) continue; switch (map->type) { case _DRM_SHM: @@ -245,12 +245,12 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, map->offset = (unsigned long)map->handle; if (map->flags & _DRM_CONTAINS_LOCK) { /* Prevent a 2nd X Server from creating a 2nd lock */ - if (dev->primary->master->lock.hw_lock != NULL) { + if (dev->master->lock.hw_lock != NULL) { vfree(map->handle); kfree(map); return -EBUSY; } - dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */ + dev->sigdata.lock = dev->master->lock.hw_lock = map->handle; /* Pointer to lock */ } break; case _DRM_AGP: { @@ -356,7 +356,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, mutex_unlock(&dev->struct_mutex); if (!(map->flags & _DRM_DRIVER)) - list->master = dev->primary->master; + list->master = dev->master; *maplist = list; return 0; } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4ec35f9e6de5..fd93e9c79d28 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -39,6 +39,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_modeset_lock.h> #include <drm/drm_atomic.h> +#include <drm/drm_auth.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -608,6 +609,31 @@ static unsigned int drm_num_crtcs(struct drm_device *dev) return num; } +static int drm_crtc_register_all(struct drm_device *dev) +{ + struct drm_crtc *crtc; + int ret = 0; + + drm_for_each_crtc(crtc, dev) { + if (crtc->funcs->late_register) + ret = crtc->funcs->late_register(crtc); + if (ret) + return ret; + } + + return 0; +} + +static void drm_crtc_unregister_all(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, dev) { + if (crtc->funcs->early_unregister) + crtc->funcs->early_unregister(crtc); + } +} + /** * drm_crtc_init_with_planes - Initialise a new CRTC object with * specified primary and cursor planes. @@ -938,6 +964,12 @@ void drm_connector_cleanup(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *t; + /* The connector should have been removed from userspace long before + * it is finally destroyed. + */ + if (WARN_ON(connector->registered)) + drm_connector_unregister(connector); + if (connector->tile_group) { drm_mode_put_tile_group(dev, connector->tile_group); connector->tile_group = NULL; @@ -984,19 +1016,34 @@ int drm_connector_register(struct drm_connector *connector) { int ret; + if (connector->registered) + return 0; + ret = drm_sysfs_connector_add(connector); if (ret) return ret; ret = drm_debugfs_connector_add(connector); if (ret) { - drm_sysfs_connector_remove(connector); - return ret; + goto err_sysfs; + } + + if (connector->funcs->late_register) { + ret = connector->funcs->late_register(connector); + if (ret) + goto err_debugfs; } drm_mode_object_register(connector->dev, &connector->base); + connector->registered = true; return 0; + +err_debugfs: + drm_debugfs_connector_remove(connector); +err_sysfs: + drm_sysfs_connector_remove(connector); + return ret; } EXPORT_SYMBOL(drm_connector_register); @@ -1008,8 +1055,16 @@ EXPORT_SYMBOL(drm_connector_register); */ void drm_connector_unregister(struct drm_connector *connector) { + if (!connector->registered) + return; + + if (connector->funcs->early_unregister) + connector->funcs->early_unregister(connector); + drm_sysfs_connector_remove(connector); drm_debugfs_connector_remove(connector); + + connector->registered = false; } EXPORT_SYMBOL(drm_connector_unregister); @@ -1018,9 +1073,9 @@ EXPORT_SYMBOL(drm_connector_unregister); * @dev: drm device * * This function registers all connectors in sysfs and other places so that - * userspace can start to access them. Drivers can call it after calling - * drm_dev_register() to complete the device registration, if they don't call - * drm_connector_register() on each connector individually. + * userspace can start to access them. drm_connector_register_all() is called + * automatically from drm_dev_register() to complete the device registration, + * if they don't call drm_connector_register() on each connector individually. * * When a device is unplugged and should be removed from userspace access, * call drm_connector_unregister_all(), which is the inverse of this @@ -1073,6 +1128,31 @@ void drm_connector_unregister_all(struct drm_device *dev) } EXPORT_SYMBOL(drm_connector_unregister_all); +static int drm_encoder_register_all(struct drm_device *dev) +{ + struct drm_encoder *encoder; + int ret = 0; + + drm_for_each_encoder(encoder, dev) { + if (encoder->funcs->late_register) + ret = encoder->funcs->late_register(encoder); + if (ret) + return ret; + } + + return 0; +} + +static void drm_encoder_unregister_all(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, dev) { + if (encoder->funcs->early_unregister) + encoder->funcs->early_unregister(encoder); + } +} + /** * drm_encoder_init - Init a preallocated encoder * @dev: drm device @@ -1261,6 +1341,31 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, } EXPORT_SYMBOL(drm_universal_plane_init); +static int drm_plane_register_all(struct drm_device *dev) +{ + struct drm_plane *plane; + int ret = 0; + + drm_for_each_plane(plane, dev) { + if (plane->funcs->late_register) + ret = plane->funcs->late_register(plane); + if (ret) + return ret; + } + + return 0; +} + +static void drm_plane_unregister_all(struct drm_device *dev) +{ + struct drm_plane *plane; + + drm_for_each_plane(plane, dev) { + if (plane->funcs->early_unregister) + plane->funcs->early_unregister(plane); + } +} + /** * drm_plane_init - Initialize a legacy plane * @dev: DRM device @@ -1383,6 +1488,46 @@ void drm_plane_force_disable(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_force_disable); +int drm_modeset_register_all(struct drm_device *dev) +{ + int ret; + + ret = drm_plane_register_all(dev); + if (ret) + goto err_plane; + + ret = drm_crtc_register_all(dev); + if (ret) + goto err_crtc; + + ret = drm_encoder_register_all(dev); + if (ret) + goto err_encoder; + + ret = drm_connector_register_all(dev); + if (ret) + goto err_connector; + + return 0; + +err_connector: + drm_encoder_unregister_all(dev); +err_encoder: + drm_crtc_unregister_all(dev); +err_crtc: + drm_plane_unregister_all(dev); +err_plane: + return ret; +} + +void drm_modeset_unregister_all(struct drm_device *dev) +{ + drm_connector_unregister_all(dev); + drm_encoder_unregister_all(dev); + drm_crtc_unregister_all(dev); + drm_plane_unregister_all(dev); +} + static int drm_mode_create_standard_properties(struct drm_device *dev) { struct drm_property *prop; @@ -3499,7 +3644,7 @@ int drm_mode_getfb(struct drm_device *dev, r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; if (fb->funcs->create_handle) { - if (file_priv->is_master || capable(CAP_SYS_ADMIN) || + if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || drm_is_control_client(file_priv)) { ret = fb->funcs->create_handle(fb, file_priv, &r->handle); @@ -3656,6 +3801,13 @@ void drm_fb_release(struct drm_file *priv) } } +static bool drm_property_type_valid(struct drm_property *property) +{ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE); + return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE); +} + /** * drm_property_create - create a new property type * @dev: drm device diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index bf10d7046aa6..d61591274ff6 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -232,6 +232,9 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) */ void drm_helper_disable_unused_functions(struct drm_device *dev) { + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) + DRM_ERROR("Called for atomic driver, this is not what you want.\n"); + drm_modeset_lock_all(dev); __drm_helper_disable_unused_functions(dev); drm_modeset_unlock_all(dev); diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index a78c138282ea..47a500b90fd7 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -31,14 +31,100 @@ * and are not exported to drivers. */ + +/* drm_crtc.c */ +void drm_connector_ida_init(void); +void drm_connector_ida_destroy(void); int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type); void drm_mode_object_unregister(struct drm_device *dev, struct drm_mode_object *object); +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, + struct drm_mode_object **ref); +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref); + +int drm_plane_check_pixel_format(const struct drm_plane *plane, + u32 format); +int drm_crtc_check_viewport(const struct drm_crtc *crtc, + int x, int y, + const struct drm_display_mode *mode, + const struct drm_framebuffer *fb); + +void drm_fb_release(struct drm_file *file_priv); +void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv); + +/* dumb buffer support IOCTLs */ +int drm_mode_create_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +/* framebuffer IOCTLs */ +extern int drm_mode_addfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_addfb2(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_rmfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_dirtyfb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +/* IOCTLs */ +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +int drm_mode_getresources(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getplane_res(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_mode_getcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getconnector(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_setcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getplane(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_setplane(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_cursor_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_cursor2_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_createblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_destroyblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_connector_property_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getencoder(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_gamma_get_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_gamma_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); /* drm_atomic.c */ int drm_atomic_get_property(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val); + struct drm_property *property, uint64_t *val); int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_modeset_register_all(struct drm_device *dev); +void drm_modeset_unregister_all(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 3bcf8e6a85b3..fa10cef2ba37 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -46,11 +46,8 @@ static const struct drm_info_list drm_debugfs_list[] = { {"name", drm_name_info, 0}, - {"vm", drm_vm_info, 0}, {"clients", drm_clients_info, 0}, - {"bufs", drm_bufs_info, 0}, {"gem_names", drm_gem_name_info, DRIVER_GEM}, - {"vma", drm_vma_info, 0}, }; #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index eeaf5a7c3aa7..091053e995e5 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -708,8 +708,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, memset(&msg, 0, sizeof(msg)); - mutex_lock(&aux->hw_mutex); - for (i = 0; i < num; i++) { msg.address = msgs[i].addr; drm_dp_i2c_msg_set_request(&msg, &msgs[i]); @@ -764,8 +762,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, msg.size = 0; (void)drm_dp_i2c_do_msg(aux, &msg); - mutex_unlock(&aux->hw_mutex); - return err; } @@ -774,22 +770,64 @@ static const struct i2c_algorithm drm_dp_i2c_algo = { .master_xfer = drm_dp_i2c_xfer, }; +static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c) +{ + return container_of(i2c, struct drm_dp_aux, ddc); +} + +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags) +{ + mutex_lock(&i2c_to_aux(i2c)->hw_mutex); +} + +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags) +{ + return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex); +} + +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags) +{ + mutex_unlock(&i2c_to_aux(i2c)->hw_mutex); +} + /** - * drm_dp_aux_register() - initialise and register aux channel + * drm_dp_aux_init() - minimally initialise an aux channel * @aux: DisplayPort AUX channel * - * Returns 0 on success or a negative error code on failure. + * If you need to use the drm_dp_aux's i2c adapter prior to registering it + * with the outside world, call drm_dp_aux_init() first. You must still + * call drm_dp_aux_register() once the connector has been registered to + * allow userspace access to the auxiliary DP channel. */ -int drm_dp_aux_register(struct drm_dp_aux *aux) +void drm_dp_aux_init(struct drm_dp_aux *aux) { - int ret; - mutex_init(&aux->hw_mutex); aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo_data = aux; aux->ddc.retries = 3; + aux->ddc.lock_bus = lock_bus; + aux->ddc.trylock_bus = trylock_bus; + aux->ddc.unlock_bus = unlock_bus; +} +EXPORT_SYMBOL(drm_dp_aux_init); + +/** + * drm_dp_aux_register() - initialise and register aux channel + * @aux: DisplayPort AUX channel + * + * Automatically calls drm_dp_aux_init() if this hasn't been done yet. + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_aux_register(struct drm_dp_aux *aux) +{ + int ret; + + if (!aux->ddc.algo) + drm_dp_aux_init(aux); + aux->ddc.class = I2C_CLASS_DDC; aux->ddc.owner = THIS_MODULE; aux->ddc.dev.parent = aux->dev; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 8b2582aeaab6..aead9ffcbe29 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -34,8 +34,10 @@ #include <linux/slab.h> #include <drm/drmP.h> #include <drm/drm_core.h> +#include "drm_crtc_internal.h" #include "drm_legacy.h" #include "drm_internal.h" +#include "drm_crtc_internal.h" /* * drm_debug: Enable debug output. @@ -93,114 +95,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...) } EXPORT_SYMBOL(drm_ut_debug_printk); -struct drm_master *drm_master_create(struct drm_minor *minor) -{ - struct drm_master *master; - - master = kzalloc(sizeof(*master), GFP_KERNEL); - if (!master) - return NULL; - - kref_init(&master->refcount); - spin_lock_init(&master->lock.spinlock); - init_waitqueue_head(&master->lock.lock_queue); - idr_init(&master->magic_map); - master->minor = minor; - - return master; -} - -struct drm_master *drm_master_get(struct drm_master *master) -{ - kref_get(&master->refcount); - return master; -} -EXPORT_SYMBOL(drm_master_get); - -static void drm_master_destroy(struct kref *kref) -{ - struct drm_master *master = container_of(kref, struct drm_master, refcount); - struct drm_device *dev = master->minor->dev; - - if (dev->driver->master_destroy) - dev->driver->master_destroy(dev, master); - - drm_legacy_master_rmmaps(dev, master); - - idr_destroy(&master->magic_map); - kfree(master->unique); - kfree(master); -} - -void drm_master_put(struct drm_master **master) -{ - kref_put(&(*master)->refcount, drm_master_destroy); - *master = NULL; -} -EXPORT_SYMBOL(drm_master_put); - -int drm_setmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - int ret = 0; - - mutex_lock(&dev->master_mutex); - if (file_priv->is_master) - goto out_unlock; - - if (file_priv->minor->master) { - ret = -EINVAL; - goto out_unlock; - } - - if (!file_priv->master) { - ret = -EINVAL; - goto out_unlock; - } - - if (!file_priv->allowed_master) { - ret = drm_new_set_master(dev, file_priv); - goto out_unlock; - } - - file_priv->minor->master = drm_master_get(file_priv->master); - file_priv->is_master = 1; - if (dev->driver->master_set) { - ret = dev->driver->master_set(dev, file_priv, false); - if (unlikely(ret != 0)) { - file_priv->is_master = 0; - drm_master_put(&file_priv->minor->master); - } - } - -out_unlock: - mutex_unlock(&dev->master_mutex); - return ret; -} - -int drm_dropmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - int ret = -EINVAL; - - mutex_lock(&dev->master_mutex); - if (!file_priv->is_master) - goto out_unlock; - - if (!file_priv->minor->master) - goto out_unlock; - - ret = 0; - if (dev->driver->master_drop) - dev->driver->master_drop(dev, file_priv, false); - drm_master_put(&file_priv->minor->master); - file_priv->is_master = 0; - -out_unlock: - mutex_unlock(&dev->master_mutex); - return ret; -} - /* * DRM Minors * A DRM device can provide several char-dev interfaces on the DRM-Major. Each @@ -405,10 +299,9 @@ void drm_minor_release(struct drm_minor *minor) * callbacks implemented by the driver. The driver then needs to initialize all * the various subsystems for the drm device like memory management, vblank * handling, modesetting support and intial output configuration plus obviously - * initialize all the corresponding hardware bits. An important part of this is - * also calling drm_dev_set_unique() to set the userspace-visible unique name of - * this device instance. Finally when everything is up and running and ready for - * userspace the device instance can be published using drm_dev_register(). + * initialize all the corresponding hardware bits. Finally when everything is up + * and running and ready for userspace the device instance can be published + * using drm_dev_register(). * * There is also deprecated support for initalizing device instances using * bus-specific helpers and the ->load() callback. But due to @@ -430,6 +323,14 @@ void drm_minor_release(struct drm_minor *minor) * dev_priv field of &drm_device. */ +static int drm_dev_set_unique(struct drm_device *dev, const char *name) +{ + kfree(dev->unique); + dev->unique = kstrdup(name, GFP_KERNEL); + + return dev->unique ? 0 : -ENOMEM; +} + /** * drm_put_dev - Unregister and release a DRM device * @dev: DRM device @@ -549,11 +450,12 @@ static void drm_fs_inode_free(struct inode *inode) } /** - * drm_dev_alloc - Allocate new DRM device - * @driver: DRM driver to allocate device for + * drm_dev_init - Initialise new DRM device + * @dev: DRM device + * @driver: DRM driver * @parent: Parent device object * - * Allocate and initialize a new DRM device. No device registration is done. + * Initialize a new DRM device. No device registration is done. * Call drm_dev_register() to advertice the device to user space and register it * with other core subsystems. This should be done last in the device * initialization sequence to make sure userspace can't access an inconsistent @@ -564,19 +466,18 @@ static void drm_fs_inode_free(struct inode *inode) * * Note that for purely virtual devices @parent can be NULL. * + * Drivers that do not want to allocate their own device struct + * embedding struct &drm_device can call drm_dev_alloc() instead. + * * RETURNS: - * Pointer to new DRM device, or NULL if out of memory. + * 0 on success, or error code on failure. */ -struct drm_device *drm_dev_alloc(struct drm_driver *driver, - struct device *parent) +int drm_dev_init(struct drm_device *dev, + struct drm_driver *driver, + struct device *parent) { - struct drm_device *dev; int ret; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - kref_init(&dev->ref); dev->dev = parent; dev->driver = driver; @@ -617,7 +518,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, if (ret) goto err_minors; - if (drm_ht_create(&dev->map_hash, 12)) + ret = drm_ht_create(&dev->map_hash, 12); + if (ret) goto err_minors; drm_legacy_ctxbitmap_init(dev); @@ -630,13 +532,13 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, } } - if (parent) { - ret = drm_dev_set_unique(dev, dev_name(parent)); - if (ret) - goto err_setunique; - } + /* Use the parent device name as DRM device unique identifier, but fall + * back to the driver name for virtual devices like vgem. */ + ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name); + if (ret) + goto err_setunique; - return dev; + return 0; err_setunique: if (drm_core_check_feature(dev, DRIVER_GEM)) @@ -651,8 +553,49 @@ err_minors: drm_fs_inode_free(dev->anon_inode); err_free: mutex_destroy(&dev->master_mutex); - kfree(dev); - return NULL; + return ret; +} +EXPORT_SYMBOL(drm_dev_init); + +/** + * drm_dev_alloc - Allocate new DRM device + * @driver: DRM driver to allocate device for + * @parent: Parent device object + * + * Allocate and initialize a new DRM device. No device registration is done. + * Call drm_dev_register() to advertice the device to user space and register it + * with other core subsystems. This should be done last in the device + * initialization sequence to make sure userspace can't access an inconsistent + * state. + * + * The initial ref-count of the object is 1. Use drm_dev_ref() and + * drm_dev_unref() to take and drop further ref-counts. + * + * Note that for purely virtual devices @parent can be NULL. + * + * Drivers that wish to subclass or embed struct &drm_device into their + * own struct should look at using drm_dev_init() instead. + * + * RETURNS: + * Pointer to new DRM device, or NULL if out of memory. + */ +struct drm_device *drm_dev_alloc(struct drm_driver *driver, + struct device *parent) +{ + struct drm_device *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + ret = drm_dev_init(dev, driver, parent); + if (ret) { + kfree(dev); + return NULL; + } + + return dev; } EXPORT_SYMBOL(drm_dev_alloc); @@ -716,11 +659,7 @@ EXPORT_SYMBOL(drm_dev_unref); * * Register the DRM device @dev with the system, advertise device to user-space * and start normal device operation. @dev must be allocated via drm_dev_alloc() - * previously. Right after drm_dev_register() the driver should call - * drm_connector_register_all() to register all connectors in sysfs. This is - * a separate call for backward compatibility with drivers still using - * the deprecated ->load() callback, where connectors are registered from within - * the ->load() callback. + * previously. * * Never call this twice on any device! * @@ -757,6 +696,9 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) goto err_minors; } + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_modeset_register_all(dev); + ret = 0; goto out_unlock; @@ -787,6 +729,9 @@ void drm_dev_unregister(struct drm_device *dev) drm_lastclose(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_modeset_unregister_all(dev); + if (dev->driver->unload) dev->driver->unload(dev); @@ -804,26 +749,6 @@ void drm_dev_unregister(struct drm_device *dev) } EXPORT_SYMBOL(drm_dev_unregister); -/** - * drm_dev_set_unique - Set the unique name of a DRM device - * @dev: device of which to set the unique name - * @name: unique name - * - * Sets the unique name of a DRM device using the specified string. Drivers - * can use this at driver probe time if the unique name of the devices they - * drive is static. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_dev_set_unique(struct drm_device *dev, const char *name) -{ - kfree(dev->unique); - dev->unique = kstrdup(name, GFP_KERNEL); - - return dev->unique ? 0 : -ENOMEM; -} -EXPORT_SYMBOL(drm_dev_set_unique); - /* * DRM Core * The DRM core module initializes all global DRM objects and makes them diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0bac5246e5a7..ce54e985d91b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -464,7 +464,7 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) /* Sometimes user space wants everything disabled, so don't steal the * display if there's a master. */ - if (dev->primary->master) + if (lockless_dereference(dev->master)) return false; drm_for_each_crtc(crtc, dev) { diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index a27bc7cda975..323c238fcac7 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -40,6 +40,7 @@ #include <linux/module.h> #include "drm_legacy.h" #include "drm_internal.h" +#include "drm_crtc_internal.h" /* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); @@ -168,60 +169,6 @@ static int drm_cpu_valid(void) } /* - * drm_new_set_master - Allocate a new master object and become master for the - * associated master realm. - * - * @dev: The associated device. - * @fpriv: File private identifying the client. - * - * This function must be called with dev::struct_mutex held. - * Returns negative error code on failure. Zero on success. - */ -int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) -{ - struct drm_master *old_master; - int ret; - - lockdep_assert_held_once(&dev->master_mutex); - - /* create a new master */ - fpriv->minor->master = drm_master_create(fpriv->minor); - if (!fpriv->minor->master) - return -ENOMEM; - - /* take another reference for the copy in the local file priv */ - old_master = fpriv->master; - fpriv->master = drm_master_get(fpriv->minor->master); - - if (dev->driver->master_create) { - ret = dev->driver->master_create(dev, fpriv->master); - if (ret) - goto out_err; - } - if (dev->driver->master_set) { - ret = dev->driver->master_set(dev, fpriv, true); - if (ret) - goto out_err; - } - - fpriv->is_master = 1; - fpriv->allowed_master = 1; - fpriv->authenticated = 1; - if (old_master) - drm_master_put(&old_master); - - return 0; - -out_err: - /* drop both references and restore old master on failure */ - drm_master_put(&fpriv->minor->master); - drm_master_put(&fpriv->master); - fpriv->master = old_master; - - return ret; -} - -/* * Called whenever a process opens /dev/drm. * * \param filp file pointer. @@ -283,19 +230,11 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) goto out_prime_destroy; } - /* if there is no current master make this fd it, but do not create - * any master object for render clients */ - mutex_lock(&dev->master_mutex); - if (drm_is_primary_client(priv) && !priv->minor->master) { - /* create a new master */ - ret = drm_new_set_master(dev, priv); + if (drm_is_primary_client(priv)) { + ret = drm_master_open(priv); if (ret) goto out_close; - } else if (drm_is_primary_client(priv)) { - /* get a reference to the master */ - priv->master = drm_master_get(priv->minor->master); } - mutex_unlock(&dev->master_mutex); mutex_lock(&dev->filelist_mutex); list_add(&priv->lhead, &dev->filelist); @@ -324,7 +263,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) return 0; out_close: - mutex_unlock(&dev->master_mutex); if (dev->driver->postclose) dev->driver->postclose(dev, priv); out_prime_destroy: @@ -338,18 +276,6 @@ out_prime_destroy: return ret; } -static void drm_master_release(struct drm_device *dev, struct file *filp) -{ - struct drm_file *file_priv = filp->private_data; - - if (drm_legacy_i_have_hw_lock(dev, file_priv)) { - DRM_DEBUG("File %p released, freeing lock for context %d\n", - filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - drm_legacy_lock_free(&file_priv->master->lock, - _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - } -} - static void drm_events_release(struct drm_file *file_priv) { struct drm_device *dev = file_priv->minor->dev; @@ -451,11 +377,6 @@ int drm_release(struct inode *inode, struct file *filp) list_del(&file_priv->lhead); mutex_unlock(&dev->filelist_mutex); - mutex_lock(&dev->struct_mutex); - if (file_priv->magic) - idr_remove(&file_priv->master->magic_map, file_priv->magic); - mutex_unlock(&dev->struct_mutex); - if (dev->driver->preclose) dev->driver->preclose(dev, file_priv); @@ -468,9 +389,8 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->kdev->devt), dev->open_count); - /* if the master has gone away we can't do anything with the lock */ - if (file_priv->minor->master) - drm_master_release(dev, filp); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + drm_legacy_lock_release(dev, filp); if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) drm_legacy_reclaim_buffers(dev, file_priv); @@ -487,43 +407,12 @@ int drm_release(struct inode *inode, struct file *filp) drm_legacy_ctxbitmap_flush(dev, file_priv); - mutex_lock(&dev->master_mutex); - - if (file_priv->is_master) { - struct drm_master *master = file_priv->master; - - /* - * Since the master is disappearing, so is the - * possibility to lock. - */ - mutex_lock(&dev->struct_mutex); - if (master->lock.hw_lock) { - if (dev->sigdata.lock == master->lock.hw_lock) - dev->sigdata.lock = NULL; - master->lock.hw_lock = NULL; - master->lock.file_priv = NULL; - wake_up_interruptible_all(&master->lock.lock_queue); - } - mutex_unlock(&dev->struct_mutex); - - if (file_priv->minor->master == file_priv->master) { - /* drop the reference held my the minor */ - if (dev->driver->master_drop) - dev->driver->master_drop(dev, file_priv, true); - drm_master_put(&file_priv->minor->master); - } - } - - /* drop the master reference held by the file priv */ - if (file_priv->master) - drm_master_put(&file_priv->master); - file_priv->is_master = 0; - mutex_unlock(&dev->master_mutex); + if (drm_is_primary_client(file_priv)) + drm_master_release(file_priv); if (dev->driver->postclose) dev->driver->postclose(dev, file_priv); - if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&file_priv->prime); diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 5d469b2f26f4..9ae353f4dd06 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -50,106 +50,24 @@ int drm_name_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_minor *minor = node->minor; struct drm_device *dev = minor->dev; - struct drm_master *master = minor->master; - if (!master) - return 0; - - if (master->unique) { - seq_printf(m, "%s %s %s\n", - dev->driver->name, - dev_name(dev->dev), master->unique); - } else { - seq_printf(m, "%s %s\n", - dev->driver->name, dev_name(dev->dev)); - } - return 0; -} - -/** - * Called when "/proc/dri/.../vm" is read. - * - * Prints information about all mappings in drm_device::maplist. - */ -int drm_vm_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_local_map *map; - struct drm_map_list *r_list; - - /* Hardcoded from _DRM_FRAME_BUFFER, - _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and - _DRM_SCATTER_GATHER and _DRM_CONSISTENT */ - const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" }; - const char *type; - int i; - - mutex_lock(&dev->struct_mutex); - seq_printf(m, "slot offset size type flags address mtrr\n\n"); - i = 0; - list_for_each_entry(r_list, &dev->maplist, head) { - map = r_list->map; - if (!map) - continue; - if (map->type < 0 || map->type > 5) - type = "??"; - else - type = types[map->type]; - - seq_printf(m, "%4d 0x%016llx 0x%08lx %4.4s 0x%02x 0x%08lx ", - i, - (unsigned long long)map->offset, - map->size, type, map->flags, - (unsigned long) r_list->user_token); - if (map->mtrr < 0) - seq_printf(m, "none\n"); - else - seq_printf(m, "%4d\n", map->mtrr); - i++; - } - mutex_unlock(&dev->struct_mutex); - return 0; -} + struct drm_master *master; -/** - * Called when "/proc/dri/.../bufs" is read. - */ -int drm_bufs_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_device_dma *dma; - int i, seg_pages; - - mutex_lock(&dev->struct_mutex); - dma = dev->dma; - if (!dma) { - mutex_unlock(&dev->struct_mutex); - return 0; - } - - seq_printf(m, " o size count free segs pages kB\n\n"); - for (i = 0; i <= DRM_MAX_ORDER; i++) { - if (dma->bufs[i].buf_count) { - seg_pages = dma->bufs[i].seg_count * (1 << dma->bufs[i].page_order); - seq_printf(m, "%2d %8d %5d %5d %5d %5d %5ld\n", - i, - dma->bufs[i].buf_size, - dma->bufs[i].buf_count, - 0, - dma->bufs[i].seg_count, - seg_pages, - seg_pages * PAGE_SIZE / 1024); - } - } - seq_printf(m, "\n"); - for (i = 0; i < dma->buf_count; i++) { - if (i && !(i % 32)) - seq_printf(m, "\n"); - seq_printf(m, " %d", dma->buflist[i]->list); - } + mutex_lock(&dev->master_mutex); + master = dev->master; + if (!master) + goto out_unlock; + + seq_printf(m, "%s", dev->driver->name); + if (dev->dev) + seq_printf(m, " dev=%s", dev_name(dev->dev)); + if (master && master->unique) + seq_printf(m, " master=%s", master->unique); + if (dev->unique) + seq_printf(m, " unique=%s", dev->unique); seq_printf(m, "\n"); - mutex_unlock(&dev->struct_mutex); +out_unlock: + mutex_unlock(&dev->master_mutex); + return 0; } @@ -184,7 +102,7 @@ int drm_clients_info(struct seq_file *m, void *data) task ? task->comm : "<unknown>", pid_vnr(priv->pid), priv->minor->index, - priv->is_master ? 'y' : 'n', + drm_is_current_master(priv) ? 'y' : 'n', priv->authenticated ? 'y' : 'n', from_kuid_munged(seq_user_ns(m), priv->uid), priv->magic); @@ -194,7 +112,6 @@ int drm_clients_info(struct seq_file *m, void *data) return 0; } - static int drm_gem_one_name_info(int id, void *ptr, void *data) { struct drm_gem_object *obj = ptr; diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 902cf6a15212..b86dc9b921a5 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -29,15 +29,9 @@ extern struct mutex drm_global_mutex; void drm_lastclose(struct drm_device *dev); /* drm_pci.c */ -int drm_pci_set_unique(struct drm_device *dev, - struct drm_master *master, - struct drm_unique *u); int drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv); -/* drm_vm.c */ -int drm_vma_info(struct seq_file *m, void *data); - /* drm_prime.c */ int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -51,8 +45,6 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr /* drm_info.c */ int drm_name_info(struct seq_file *m, void *data); -int drm_vm_info(struct seq_file *m, void *data); -int drm_bufs_info(struct seq_file *m, void *data); int drm_clients_info(struct seq_file *m, void* data); int drm_gem_name_info(struct seq_file *m, void *data); @@ -67,6 +59,12 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_master_open(struct drm_file *file_priv); +void drm_master_release(struct drm_file *file_priv); /* drm_sysfs.c */ extern struct class *drm_class; @@ -92,13 +90,6 @@ int drm_gem_open_ioctl(struct drm_device *dev, void *data, void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); -/* drm_drv.c */ -int drm_setmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_dropmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -struct drm_master *drm_master_create(struct drm_minor *minor); - /* drm_debugfs.c */ #if defined(CONFIG_DEBUG_FS) int drm_debugfs_init(struct drm_minor *minor, int minor_id, diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index b7a39771c152..1f84ff5f1bf8 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -30,6 +30,7 @@ #include <drm/drmP.h> #include <drm/drm_core.h> +#include <drm/drm_auth.h> #include "drm_legacy.h" #include "drm_internal.h" #include "drm_crtc_internal.h" @@ -37,6 +38,64 @@ #include <linux/pci.h> #include <linux/export.h> +/** + * DOC: getunique and setversion story + * + * BEWARE THE DRAGONS! MIND THE TRAPDOORS! + * + * In an attempt to warn anyone else who's trying to figure out what's going + * on here, I'll try to summarize the story. First things first, let's clear up + * the names, because the kernel internals, libdrm and the ioctls are all named + * differently: + * + * - GET_UNIQUE ioctl, implemented by drm_getunique is wrapped up in libdrm + * through the drmGetBusid function. + * - The libdrm drmSetBusid function is backed by the SET_UNIQUE ioctl. All + * that code is nerved in the kernel with drm_invalid_op(). + * - The internal set_busid kernel functions and driver callbacks are + * exclusively use by the SET_VERSION ioctl, because only drm 1.0 (which is + * nerved) allowed userspace to set the busid through the above ioctl. + * - Other ioctls and functions involved are named consistently. + * + * For anyone wondering what's the difference between drm 1.1 and 1.4: Correctly + * handling pci domains in the busid on ppc. Doing this correctly was only + * implemented in libdrm in 2010, hence can't be nerved yet. No one knows what's + * special with drm 1.2 and 1.3. + * + * Now the actual horror story of how device lookup in drm works. At large, + * there's 2 different ways, either by busid, or by device driver name. + * + * Opening by busid is fairly simple: + * + * 1. First call SET_VERSION to make sure pci domains are handled properly. As a + * side-effect this fills out the unique name in the master structure. + * 2. Call GET_UNIQUE to read out the unique name from the master structure, + * which matches the busid thanks to step 1. If it doesn't, proceed to try + * the next device node. + * + * Opening by name is slightly different: + * + * 1. Directly call VERSION to get the version and to match against the driver + * name returned by that ioctl. Note that SET_VERSION is not called, which + * means the the unique name for the master node just opening is _not_ filled + * out. This despite that with current drm device nodes are always bound to + * one device, and can't be runtime assigned like with drm 1.0. + * 2. Match driver name. If it mismatches, proceed to the next device node. + * 3. Call GET_UNIQUE, and check whether the unique name has length zero (by + * checking that the first byte in the string is 0). If that's not the case + * libdrm skips and proceeds to the next device node. Probably this is just + * copypasta from drm 1.0 times where a set unique name meant that the driver + * was in use already, but that's just conjecture. + * + * Long story short: To keep the open by name logic working, GET_UNIQUE must + * _not_ return a unique string when SET_VERSION hasn't been called yet, + * otherwise libdrm breaks. Even when that unique string can't ever change, and + * is totally irrelevant for actually opening the device because runtime + * assignable device instances were only support in drm 1.0, which is long dead. + * But the libdrm code in drmOpenByName somehow survived, hence this can't be + * broken. + */ + static int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -75,51 +134,6 @@ drm_unset_busid(struct drm_device *dev, master->unique_len = 0; } -/* - * Set the bus id. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_unique structure. - * \return zero on success or a negative number on failure. - * - * Copies the bus id from userspace into drm_device::unique, and verifies that - * it matches the device this DRM is attached to (EINVAL otherwise). Deprecated - * in interface version 1.1 and will return EBUSY when setversion has requested - * version 1.1 or greater. Also note that KMS is all version 1.1 and later and - * UMS was only ever supported on pci devices. - */ -static int drm_setunique(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_unique *u = data; - struct drm_master *master = file_priv->master; - int ret; - - if (master->unique_len || master->unique) - return -EBUSY; - - if (!u->unique_len || u->unique_len > 1024) - return -EINVAL; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return 0; - - if (WARN_ON(!dev->pdev)) - return -EINVAL; - - ret = drm_pci_set_unique(dev, master, u); - if (ret) - goto err; - - return 0; - -err: - drm_unset_busid(dev, master); - return ret; -} - static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) { struct drm_master *master = file_priv->master; @@ -135,12 +149,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) return ret; } } else { - if (WARN(dev->unique == NULL, - "No drm_driver.set_busid() implementation provided by " - "%ps. Use drm_dev_set_unique() to set the unique " - "name explicitly.", dev->driver)) - return -EINVAL; - + WARN_ON(!dev->unique); master->unique = kstrdup(dev->unique, GFP_KERNEL); if (master->unique) master->unique_len = strlen(dev->unique); @@ -473,7 +482,8 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) return -EACCES; /* MASTER is only for master or control clients */ - if (unlikely((flags & DRM_MASTER) && !file_priv->is_master && + if (unlikely((flags & DRM_MASTER) && + !drm_is_current_master(file_priv) && !drm_is_control_client(file_priv))) return -EACCES; @@ -504,7 +514,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), - DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), + DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_legacy_getmap_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), @@ -513,10 +523,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), - DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), + DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_UNLOCKED|DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH), @@ -524,8 +534,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 76e39c50c90c..8ca3d2bf2bda 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -994,10 +994,10 @@ static void send_vblank_event(struct drm_device *dev, e->event.tv_sec = now->tv_sec; e->event.tv_usec = now->tv_usec; - drm_send_event_locked(dev, &e->base); - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, e->event.sequence); + + drm_send_event_locked(dev, &e->base); } /** diff --git a/drivers/gpu/drm/drm_legacy.h b/drivers/gpu/drm/drm_legacy.h index d3b6ee357a2b..c6f422e879dd 100644 --- a/drivers/gpu/drm/drm_legacy.h +++ b/drivers/gpu/drm/drm_legacy.h @@ -88,14 +88,10 @@ struct drm_agp_mem { struct list_head head; }; -/* - * Generic Userspace Locking-API - */ - -int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f); +/* drm_lock.c */ int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx); +void drm_legacy_lock_release(struct drm_device *dev, struct file *filp); /* DMA support */ int drm_legacy_dma_setup(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index daa2ff12101b..48ac0ebbd663 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -41,6 +41,110 @@ static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); /** + * Take the heavyweight lock. + * + * \param lock lock pointer. + * \param context locking context. + * \return one if the lock is held, or zero otherwise. + * + * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction. + */ +static +int drm_lock_take(struct drm_lock_data *lock_data, + unsigned int context) +{ + unsigned int old, new, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; + + spin_lock_bh(&lock_data->spinlock); + do { + old = *lock; + if (old & _DRM_LOCK_HELD) + new = old | _DRM_LOCK_CONT; + else { + new = context | _DRM_LOCK_HELD | + ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ? + _DRM_LOCK_CONT : 0); + } + prev = cmpxchg(lock, old, new); + } while (prev != old); + spin_unlock_bh(&lock_data->spinlock); + + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; + } + } + + if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) { + /* Have lock */ + return 1; + } + return 0; +} + +/** + * This takes a lock forcibly and hands it to context. Should ONLY be used + * inside *_unlock to give lock to kernel before calling *_dma_schedule. + * + * \param dev DRM device. + * \param lock lock pointer. + * \param context locking context. + * \return always one. + * + * Resets the lock file pointer. + * Marks the lock as held by the given context, via the \p cmpxchg instruction. + */ +static int drm_lock_transfer(struct drm_lock_data *lock_data, + unsigned int context) +{ + unsigned int old, new, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; + + lock_data->file_priv = NULL; + do { + old = *lock; + new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + return 1; +} + +static int drm_legacy_lock_free(struct drm_lock_data *lock_data, + unsigned int context) +{ + unsigned int old, new, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; + + spin_lock_bh(&lock_data->spinlock); + if (lock_data->kernel_waiters != 0) { + drm_lock_transfer(lock_data, 0); + lock_data->idle_has_lock = 1; + spin_unlock_bh(&lock_data->spinlock); + return 1; + } + spin_unlock_bh(&lock_data->spinlock); + + do { + old = *lock; + new = _DRM_LOCKING_CONTEXT(old); + prev = cmpxchg(lock, old, new); + } while (prev != old); + + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d\n", + context, _DRM_LOCKING_CONTEXT(old)); + return 1; + } + wake_up_interruptible(&lock_data->lock_queue); + return 0; +} + +/** * Lock ioctl. * * \param inode device inode. @@ -115,7 +219,7 @@ int drm_legacy_lock(struct drm_device *dev, void *data, /* don't set the block all signals on the master process for now * really probably not the correct answer but lets us debug xkb * xserver for now */ - if (!file_priv->is_master) { + if (!drm_is_current_master(file_priv)) { dev->sigdata.context = lock->context; dev->sigdata.lock = master->lock.hw_lock; } @@ -165,120 +269,6 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_ } /** - * Take the heavyweight lock. - * - * \param lock lock pointer. - * \param context locking context. - * \return one if the lock is held, or zero otherwise. - * - * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction. - */ -static -int drm_lock_take(struct drm_lock_data *lock_data, - unsigned int context) -{ - unsigned int old, new, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - spin_lock_bh(&lock_data->spinlock); - do { - old = *lock; - if (old & _DRM_LOCK_HELD) - new = old | _DRM_LOCK_CONT; - else { - new = context | _DRM_LOCK_HELD | - ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ? - _DRM_LOCK_CONT : 0); - } - prev = cmpxchg(lock, old, new); - } while (prev != old); - spin_unlock_bh(&lock_data->spinlock); - - if (_DRM_LOCKING_CONTEXT(old) == context) { - if (old & _DRM_LOCK_HELD) { - if (context != DRM_KERNEL_CONTEXT) { - DRM_ERROR("%d holds heavyweight lock\n", - context); - } - return 0; - } - } - - if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) { - /* Have lock */ - return 1; - } - return 0; -} - -/** - * This takes a lock forcibly and hands it to context. Should ONLY be used - * inside *_unlock to give lock to kernel before calling *_dma_schedule. - * - * \param dev DRM device. - * \param lock lock pointer. - * \param context locking context. - * \return always one. - * - * Resets the lock file pointer. - * Marks the lock as held by the given context, via the \p cmpxchg instruction. - */ -static int drm_lock_transfer(struct drm_lock_data *lock_data, - unsigned int context) -{ - unsigned int old, new, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - lock_data->file_priv = NULL; - do { - old = *lock; - new = context | _DRM_LOCK_HELD; - prev = cmpxchg(lock, old, new); - } while (prev != old); - return 1; -} - -/** - * Free lock. - * - * \param dev DRM device. - * \param lock lock. - * \param context context. - * - * Resets the lock file pointer. - * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task - * waiting on the lock queue. - */ -int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context) -{ - unsigned int old, new, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - spin_lock_bh(&lock_data->spinlock); - if (lock_data->kernel_waiters != 0) { - drm_lock_transfer(lock_data, 0); - lock_data->idle_has_lock = 1; - spin_unlock_bh(&lock_data->spinlock); - return 1; - } - spin_unlock_bh(&lock_data->spinlock); - - do { - old = *lock; - new = _DRM_LOCKING_CONTEXT(old); - prev = cmpxchg(lock, old, new); - } while (prev != old); - - if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { - DRM_ERROR("%d freed heavyweight lock held by %d\n", - context, _DRM_LOCKING_CONTEXT(old)); - return 1; - } - wake_up_interruptible(&lock_data->lock_queue); - return 0; -} - -/** * This function returns immediately and takes the hw lock * with the kernel context if it is free, otherwise it gets the highest priority when and if * it is eventually released. @@ -330,11 +320,27 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock_data) } EXPORT_SYMBOL(drm_legacy_idlelock_release); -int drm_legacy_i_have_hw_lock(struct drm_device *dev, - struct drm_file *file_priv) +static int drm_legacy_i_have_hw_lock(struct drm_device *dev, + struct drm_file *file_priv) { struct drm_master *master = file_priv->master; return (file_priv->lock_count && master->lock.hw_lock && _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && master->lock.file_priv == file_priv); } + +void drm_legacy_lock_release(struct drm_device *dev, struct file *filp) +{ + struct drm_file *file_priv = filp->private_data; + + /* if the master has gone away we can't do anything with the lock */ + if (!dev->master) + return; + + if (drm_legacy_i_have_hw_lock(dev, file_priv)) { + DRM_DEBUG("File %p released, freeing lock for context %d\n", + filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + drm_legacy_lock_free(&file_priv->master->lock, + _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + } +} diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 29d5a548d07a..b2f8f1062d5f 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -144,50 +144,6 @@ int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) } EXPORT_SYMBOL(drm_pci_set_busid); -int drm_pci_set_unique(struct drm_device *dev, - struct drm_master *master, - struct drm_unique *u) -{ - int domain, bus, slot, func, ret; - - master->unique_len = u->unique_len; - master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); - if (!master->unique) { - ret = -ENOMEM; - goto err; - } - - if (copy_from_user(master->unique, u->unique, master->unique_len)) { - ret = -EFAULT; - goto err; - } - - master->unique[master->unique_len] = '\0'; - - /* Return error if the busid submitted doesn't match the device's actual - * busid. - */ - ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); - if (ret != 3) { - ret = -EINVAL; - goto err; - } - - domain = bus >> 8; - bus &= 0xff; - - if ((domain != drm_get_pci_domain(dev)) || - (bus != dev->pdev->bus->number) || - (slot != PCI_SLOT(dev->pdev->devfn)) || - (func != PCI_FUNC(dev->pdev->devfn))) { - ret = -EINVAL; - goto err; - } - return 0; -err: - return ret; -} - static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) { if ((p->busnum >> 8) != drm_get_pci_domain(dev) || @@ -444,13 +400,6 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, { return -EINVAL; } - -int drm_pci_set_unique(struct drm_device *dev, - struct drm_master *master, - struct drm_unique *u) -{ - return -EINVAL; -} #endif EXPORT_SYMBOL(drm_pci_init); diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index fc51306fe365..16c4a7bd7465 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -115,6 +115,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, * @src: source coordinates in 16.16 fixed point * @dest: integer destination coordinates * @clip: integer clipping coordinates + * @rotation: plane rotation * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point * @can_position: is it legal to position the plane such that it @@ -134,16 +135,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, * Zero if update appears valid, error code on failure */ int drm_plane_helper_check_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_rect *src, - struct drm_rect *dest, - const struct drm_rect *clip, - int min_scale, - int max_scale, - bool can_position, - bool can_update_disabled, - bool *visible) + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_rect *src, + struct drm_rect *dest, + const struct drm_rect *clip, + unsigned int rotation, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled, + bool *visible) { int hscale, vscale; @@ -163,6 +165,8 @@ int drm_plane_helper_check_update(struct drm_plane *plane, return -EINVAL; } + drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation); + /* Check scaling */ hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale); vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale); @@ -174,6 +178,9 @@ int drm_plane_helper_check_update(struct drm_plane *plane, } *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale); + + drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); + if (!*visible) /* * Plane isn't visible; some drivers can handle this @@ -267,6 +274,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, + BIT(DRM_ROTATE_0), DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, false, &visible); diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 644169e1a029..2c819ef90090 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -68,24 +68,6 @@ err_free: return ret; } -int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master) -{ - int id; - - id = dev->platformdev->id; - if (id < 0) - id = 0; - - master->unique = kasprintf(GFP_KERNEL, "platform:%s:%02d", - dev->platformdev->name, id); - if (!master->unique) - return -ENOMEM; - - master->unique_len = strlen(master->unique); - return 0; -} -EXPORT_SYMBOL(drm_platform_set_busid); - /** * drm_platform_init - Register a platform device with the DRM subsystem * @driver: DRM device driver diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index b2071d495ada..0db36d27e90b 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -105,6 +105,7 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, ret = drm_plane_helper_check_update(plane, &pipe->crtc, plane_state->fb, &src, &dest, &clip, + plane_state->rotation, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, true, &visible); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index ac9f4b3ec615..43ff44a2b8e7 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -670,57 +670,3 @@ void drm_legacy_vma_flush(struct drm_device *dev) kfree(vma); } } - -int drm_vma_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_vma_entry *pt; - struct vm_area_struct *vma; - unsigned long vma_count = 0; -#if defined(__i386__) - unsigned int pgprot; -#endif - - mutex_lock(&dev->struct_mutex); - list_for_each_entry(pt, &dev->vmalist, head) - vma_count++; - - seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n", - vma_count, high_memory, - (void *)(unsigned long)virt_to_phys(high_memory)); - - list_for_each_entry(pt, &dev->vmalist, head) { - vma = pt->vma; - if (!vma) - continue; - seq_printf(m, - "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000", - pt->pid, - (void *)vma->vm_start, (void *)vma->vm_end, - vma->vm_flags & VM_READ ? 'r' : '-', - vma->vm_flags & VM_WRITE ? 'w' : '-', - vma->vm_flags & VM_EXEC ? 'x' : '-', - vma->vm_flags & VM_MAYSHARE ? 's' : 'p', - vma->vm_flags & VM_LOCKED ? 'l' : '-', - vma->vm_flags & VM_IO ? 'i' : '-', - vma->vm_pgoff); - -#if defined(__i386__) - pgprot = pgprot_val(vma->vm_page_prot); - seq_printf(m, " %c%c%c%c%c%c%c%c%c", - pgprot & _PAGE_PRESENT ? 'p' : '-', - pgprot & _PAGE_RW ? 'w' : 'r', - pgprot & _PAGE_USER ? 'u' : 's', - pgprot & _PAGE_PWT ? 't' : 'b', - pgprot & _PAGE_PCD ? 'u' : 'c', - pgprot & _PAGE_ACCESSED ? 'a' : '-', - pgprot & _PAGE_DIRTY ? 'd' : '-', - pgprot & _PAGE_PSE ? 'm' : 'k', - pgprot & _PAGE_GLOBAL ? 'g' : 'l'); -#endif - seq_printf(m, "\n"); - } - mutex_unlock(&dev->struct_mutex); - return 0; -} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 3d4f56df8359..340d390306d8 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -496,7 +496,6 @@ static struct drm_driver etnaviv_drm_driver = { DRIVER_RENDER, .open = etnaviv_open, .preclose = etnaviv_preclose, - .set_busid = drm_platform_set_busid, .gem_free_object_unlocked = etnaviv_gem_free_object, .gem_vm_ops = &vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 4a679fb9bb02..13d28d4229e2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -407,7 +407,6 @@ static struct drm_driver exynos_drm_driver = { .preclose = exynos_drm_preclose, .lastclose = exynos_drm_lastclose, .postclose = exynos_drm_postclose, - .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = exynos_drm_crtc_enable_vblank, .disable_vblank = exynos_drm_crtc_disable_vblank, diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c index c564ec612b59..a6e4cd591960 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c @@ -37,23 +37,22 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) ret = fsl_dcu_drm_crtc_create(fsl_dev); if (ret) - return ret; + goto err; ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc); if (ret) - goto fail_encoder; + goto err; ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); if (ret) - goto fail_connector; + goto err; drm_mode_config_reset(fsl_dev->drm); drm_kms_helper_poll_init(fsl_dev->drm); return 0; -fail_encoder: - fsl_dev->crtc.funcs->destroy(&fsl_dev->crtc); -fail_connector: - fsl_dev->encoder.funcs->destroy(&fsl_dev->encoder); + +err: + drm_mode_config_cleanup(fsl_dev->drm); return ret; } diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 193657259ee9..1edd9bc80294 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -171,7 +171,6 @@ static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC | DRIVER_HAVE_IRQ, .fops = &kirin_drm_fops, - .set_busid = drm_platform_set_busid, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, @@ -221,19 +220,12 @@ static int kirin_drm_bind(struct device *dev) if (ret) goto err_kms_cleanup; - /* connectors should be registered after drm device register */ - ret = drm_connector_register_all(drm_dev); - if (ret) - goto err_drm_dev_unregister; - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, driver->major, driver->minor, driver->patchlevel, driver->date, drm_dev->primary->index); return 0; -err_drm_dev_unregister: - drm_dev_unregister(drm_dev); err_kms_cleanup: kirin_drm_kms_cleanup(drm_dev); err_drm_dev_unref: @@ -246,7 +238,6 @@ static void kirin_drm_unbind(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - drm_connector_unregister_all(drm_dev); drm_dev_unregister(drm_dev); kirin_drm_kms_cleanup(drm_dev); drm_dev_unref(drm_dev); diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 29a32b11953b..7769e469118f 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -57,6 +57,28 @@ config DRM_I915_USERPTR If in doubt, say "Y". +config DRM_I915_GVT + bool "Enable Intel GVT-g graphics virtualization host support" + depends on DRM_I915 + default n + help + Choose this option if you want to enable Intel GVT-g graphics + virtualization technology host support with integrated graphics. + With GVT-g, it's possible to have one integrated graphics + device shared by multiple VMs under different hypervisors. + + Note that at least one hypervisor like Xen or KVM is required for + this driver to work, and it only supports newer device from + Broadwell+. For further information and setup guide, you can + visit: http://01.org/igvt-g. + + Now it's just a stub to support the modifications of i915 for + GVT device model. It requires at least one MPT modules for Xen/KVM + and other components of GVT device model to work. Use it under + you own risk. + + If in doubt, say "N". + menu "drm/i915 Debugging" depends on DRM_I915 depends on EXPERT diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 7e2944406b8f..276abf1cac2b 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -104,6 +104,11 @@ i915-y += i915_vgpu.o # legacy horrors i915-y += i915_dma.o +ifeq ($(CONFIG_DRM_I915_GVT),y) +i915-y += intel_gvt.o +include $(src)/gvt/Makefile +endif + obj-$(CONFIG_DRM_I915) += i915.o CFLAGS_i915_trace_points.o := -I$(src) diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile new file mode 100644 index 000000000000..d0f21a6ad60d --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/Makefile @@ -0,0 +1,5 @@ +GVT_DIR := gvt +GVT_SOURCE := gvt.o + +ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) -Wall +i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE)) diff --git a/drivers/gpu/drm/i915/gvt/debug.h b/drivers/gpu/drm/i915/gvt/debug.h new file mode 100644 index 000000000000..7ef412be665f --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/debug.h @@ -0,0 +1,34 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GVT_DEBUG_H__ +#define __GVT_DEBUG_H__ + +#define gvt_dbg_core(fmt, args...) \ + DRM_DEBUG_DRIVER("gvt: core: "fmt, ##args) + +/* + * Other GVT debug stuff will be introduced in the GVT device model patches. + */ + +#endif diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c new file mode 100644 index 000000000000..927f4579f5b6 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -0,0 +1,145 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/types.h> +#include <xen/xen.h> + +#include "i915_drv.h" + +struct intel_gvt_host intel_gvt_host; + +static const char * const supported_hypervisors[] = { + [INTEL_GVT_HYPERVISOR_XEN] = "XEN", + [INTEL_GVT_HYPERVISOR_KVM] = "KVM", +}; + +/** + * intel_gvt_init_host - Load MPT modules and detect if we're running in host + * @gvt: intel gvt device + * + * This function is called at the driver loading stage. If failed to find a + * loadable MPT module or detect currently we're running in a VM, then GVT-g + * will be disabled + * + * Returns: + * Zero on success, negative error code if failed. + * + */ +int intel_gvt_init_host(void) +{ + if (intel_gvt_host.initialized) + return 0; + + /* Xen DOM U */ + if (xen_domain() && !xen_initial_domain()) + return -ENODEV; + + /* Try to load MPT modules for hypervisors */ + if (xen_initial_domain()) { + /* In Xen dom0 */ + intel_gvt_host.mpt = try_then_request_module( + symbol_get(xengt_mpt), "xengt"); + intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_XEN; + } else { + /* not in Xen. Try KVMGT */ + intel_gvt_host.mpt = try_then_request_module( + symbol_get(kvmgt_mpt), "kvm"); + intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_KVM; + } + + /* Fail to load MPT modules - bail out */ + if (!intel_gvt_host.mpt) + return -EINVAL; + + /* Try to detect if we're running in host instead of VM. */ + if (!intel_gvt_hypervisor_detect_host()) + return -ENODEV; + + gvt_dbg_core("Running with hypervisor %s in host mode\n", + supported_hypervisors[intel_gvt_host.hypervisor_type]); + + intel_gvt_host.initialized = true; + return 0; +} + +static void init_device_info(struct intel_gvt *gvt) +{ + if (IS_BROADWELL(gvt->dev_priv)) + gvt->device_info.max_support_vgpus = 8; + /* This function will grow large in GVT device model patches. */ +} + +/** + * intel_gvt_clean_device - clean a GVT device + * @gvt: intel gvt device + * + * This function is called at the driver unloading stage, to free the + * resources owned by a GVT device. + * + */ +void intel_gvt_clean_device(struct drm_i915_private *dev_priv) +{ + struct intel_gvt *gvt = &dev_priv->gvt; + + if (WARN_ON(!gvt->initialized)) + return; + + /* Other de-initialization of GVT components will be introduced. */ + + gvt->initialized = false; +} + +/** + * intel_gvt_init_device - initialize a GVT device + * @dev_priv: drm i915 private data + * + * This function is called at the initialization stage, to initialize + * necessary GVT components. + * + * Returns: + * Zero on success, negative error code if failed. + * + */ +int intel_gvt_init_device(struct drm_i915_private *dev_priv) +{ + struct intel_gvt *gvt = &dev_priv->gvt; + /* + * Cannot initialize GVT device without intel_gvt_host gets + * initialized first. + */ + if (WARN_ON(!intel_gvt_host.initialized)) + return -EINVAL; + + if (WARN_ON(gvt->initialized)) + return -EEXIST; + + gvt_dbg_core("init gvt device\n"); + + init_device_info(gvt); + /* + * Other initialization of GVT components will be introduce here. + */ + gvt_dbg_core("gvt device creation is done\n"); + gvt->initialized = true; + return 0; +} diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h new file mode 100644 index 000000000000..fb619a6e519d --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -0,0 +1,69 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GVT_H_ +#define _GVT_H_ + +#include "debug.h" +#include "hypercall.h" + +#define GVT_MAX_VGPU 8 + +enum { + INTEL_GVT_HYPERVISOR_XEN = 0, + INTEL_GVT_HYPERVISOR_KVM, +}; + +struct intel_gvt_host { + bool initialized; + int hypervisor_type; + struct intel_gvt_mpt *mpt; +}; + +extern struct intel_gvt_host intel_gvt_host; + +/* Describe per-platform limitations. */ +struct intel_gvt_device_info { + u32 max_support_vgpus; + /* This data structure will grow bigger in GVT device model patches */ +}; + +struct intel_vgpu { + struct intel_gvt *gvt; + int id; + unsigned long handle; /* vGPU handle used by hypervisor MPT modules */ +}; + +struct intel_gvt { + struct mutex lock; + bool initialized; + + struct drm_i915_private *dev_priv; + struct idr vgpu_idr; /* vGPU IDR pool */ + + struct intel_gvt_device_info device_info; +}; + +#include "mpt.h" + +#endif diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h new file mode 100644 index 000000000000..254df8bf1f35 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -0,0 +1,38 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GVT_HYPERCALL_H_ +#define _GVT_HYPERCALL_H_ + +/* + * Specific GVT-g MPT modules function collections. Currently GVT-g supports + * both Xen and KVM by providing dedicated hypervisor-related MPT modules. + */ +struct intel_gvt_mpt { + int (*detect_host)(void); +}; + +extern struct intel_gvt_mpt xengt_mpt; +extern struct intel_gvt_mpt kvmgt_mpt; + +#endif /* _GVT_HYPERCALL_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h new file mode 100644 index 000000000000..03601e3ffa7c --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -0,0 +1,49 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GVT_MPT_H_ +#define _GVT_MPT_H_ + +/** + * DOC: Hypervisor Service APIs for GVT-g Core Logic + * + * This is the glue layer between specific hypervisor MPT modules and GVT-g core + * logic. Each kind of hypervisor MPT module provides a collection of function + * callbacks and will be attached to GVT host when the driver is loading. + * GVT-g core logic will call these APIs to request specific services from + * hypervisor. + */ + +/** + * intel_gvt_hypervisor_detect_host - check if GVT-g is running within + * hypervisor host/privilged domain + * + * Returns: + * Zero on success, -ENODEV if current kernel is running inside a VM + */ +static inline int intel_gvt_hypervisor_detect_host(void) +{ + return intel_gvt_host.mpt->detect_host(); +} + +#endif /* _GVT_MPT_H_ */ diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index d97f28bfa9db..b0fd6a7b0603 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -737,7 +737,7 @@ static void fini_hash_table(struct intel_engine_cs *engine) /** * i915_cmd_parser_init_ring() - set cmd parser related fields for a ringbuffer - * @ring: the ringbuffer to initialize + * @engine: the engine to initialize * * Optionally initializes fields related to batch buffer command parsing in the * struct intel_engine_cs based on whether the platform requires software @@ -830,7 +830,7 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *engine) /** * i915_cmd_parser_fini_ring() - clean up cmd parser related fields - * @ring: the ringbuffer to clean up + * @engine: the engine to clean up * * Releases any resources related to command parsing that may have been * initialized for the specified ring. @@ -1024,7 +1024,7 @@ unpin_src: /** * i915_needs_cmd_parser() - should a given ring use software command parsing? - * @ring: the ring in question + * @engine: the engine in question * * Only certain platforms require software batch buffer command parsing, and * only when enabled via module parameter. @@ -1176,7 +1176,7 @@ static bool check_cmd(const struct intel_engine_cs *engine, /** * i915_parse_cmds() - parse a submitted batch buffer for privilege violations - * @ring: the ring on which the batch is to execute + * @engine: the engine on which the batch is to execute * @batch_obj: the batch buffer in question * @shadow_batch_obj: copy of the batch buffer in question * @batch_start_offset: byte offset in the batch at which execution starts @@ -1281,6 +1281,7 @@ int i915_parse_cmds(struct intel_engine_cs *engine, /** * i915_cmd_parser_get_version() - get the cmd parser version number + * @dev_priv: i915 device private * * The cmd parser maintains a simple increasing integer version number suitable * for passing to userspace clients to determine what operations are permitted. diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 614ac085e51f..5b7526697838 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2574,6 +2574,10 @@ static int i915_guc_info(struct seq_file *m, void *data) mutex_unlock(&dev->struct_mutex); + seq_printf(m, "Doorbell map:\n"); + seq_printf(m, "\t%*pb\n", GUC_MAX_DOORBELLS, guc.doorbell_bitmap); + seq_printf(m, "Doorbell next cacheline: 0x%x\n\n", guc.db_cacheline); + seq_printf(m, "GuC total action count: %llu\n", guc.action_count); seq_printf(m, "GuC action failure count: %u\n", guc.action_fail); seq_printf(m, "GuC last action command: 0x%x\n", guc.action_cmd); @@ -5306,6 +5310,10 @@ static int i915_sseu_status(struct seq_file *m, void *unused) INTEL_INFO(dev)->eu_total); seq_printf(m, " Available EU Per Subslice: %u\n", INTEL_INFO(dev)->eu_per_subslice); + seq_printf(m, " Has Pooled EU: %s\n", yesno(HAS_POOLED_EU(dev))); + if (HAS_POOLED_EU(dev)) + seq_printf(m, " Min EU in pool: %u\n", + INTEL_INFO(dev)->min_eu_in_pool); seq_printf(m, " Has Slice Power Gating: %s\n", yesno(INTEL_INFO(dev)->has_slice_pg)); seq_printf(m, " Has Subslice Power Gating: %s\n", diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 07edaed9d5a2..d15a461fa84a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -764,6 +764,32 @@ static void gen9_sseu_info_init(struct drm_device *dev) (info->slice_total > 1)); info->has_subslice_pg = (IS_BROXTON(dev) && (info->subslice_total > 1)); info->has_eu_pg = (info->eu_per_subslice > 2); + + if (IS_BROXTON(dev)) { +#define IS_SS_DISABLED(_ss_disable, ss) (_ss_disable & (0x1 << ss)) + /* + * There is a HW issue in 2x6 fused down parts that requires + * Pooled EU to be enabled as a WA. The pool configuration + * changes depending upon which subslice is fused down. This + * doesn't affect if the device has all 3 subslices enabled. + */ + /* WaEnablePooledEuFor2x6:bxt */ + info->has_pooled_eu = ((info->subslice_per_slice == 3) || + (info->subslice_per_slice == 2 && + INTEL_REVID(dev) < BXT_REVID_C0)); + + info->min_eu_in_pool = 0; + if (info->has_pooled_eu) { + if (IS_SS_DISABLED(ss_disable, 0) || + IS_SS_DISABLED(ss_disable, 2)) + info->min_eu_in_pool = 3; + else if (IS_SS_DISABLED(ss_disable, 1)) + info->min_eu_in_pool = 6; + else + info->min_eu_in_pool = 9; + } +#undef IS_SS_DISABLED + } } static void broadwell_sseu_info_init(struct drm_device *dev) @@ -962,6 +988,9 @@ static void intel_device_info_runtime_init(struct drm_device *dev) DRM_DEBUG_DRIVER("subslice per slice: %u\n", info->subslice_per_slice); DRM_DEBUG_DRIVER("EU total: %u\n", info->eu_total); DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->eu_per_subslice); + DRM_DEBUG_DRIVER("Has Pooled EU: %s\n", HAS_POOLED_EU(dev) ? "y" : "n"); + if (HAS_POOLED_EU(dev)) + DRM_DEBUG_DRIVER("Min EU in pool: %u\n", info->min_eu_in_pool); DRM_DEBUG_DRIVER("has slice power gating: %s\n", info->has_slice_pg ? "y" : "n"); DRM_DEBUG_DRIVER("has subslice power gating: %s\n", @@ -1091,6 +1120,10 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, if (ret < 0) return ret; + ret = intel_gvt_init(dev_priv); + if (ret < 0) + goto err_workqueues; + /* This must be called before any calls to HAS_PCH_* */ intel_detect_pch(dev); @@ -1116,6 +1149,10 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, "It may not be fully functional.\n"); return 0; + +err_workqueues: + i915_workqueues_cleanup(dev_priv); + return ret; } /** @@ -1487,6 +1524,8 @@ int i915_driver_unload(struct drm_device *dev) intel_fbdev_fini(dev); + intel_gvt_cleanup(dev_priv); + ret = i915_gem_suspend(dev); if (ret) { DRM_ERROR("failed to idle hardware: %d\n", ret); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 872c60608dbd..3eb47fbcea73 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -355,6 +355,7 @@ static const struct intel_device_info intel_broxton_info = { .has_ddi = 1, .has_fpga_dbg = 1, .has_fbc = 1, + .has_pooled_eu = 0, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, BDW_COLORS, @@ -517,8 +518,10 @@ void intel_detect_pch(struct drm_device *dev) } else if ((id == INTEL_PCH_P2X_DEVICE_ID_TYPE) || (id == INTEL_PCH_P3X_DEVICE_ID_TYPE) || ((id == INTEL_PCH_QEMU_DEVICE_ID_TYPE) && - pch->subsystem_vendor == 0x1af4 && - pch->subsystem_device == 0x1100)) { + pch->subsystem_vendor == + PCI_SUBVENDOR_ID_REDHAT_QUMRANET && + pch->subsystem_device == + PCI_SUBDEVICE_ID_QEMU)) { dev_priv->pch_type = intel_virt_detect_pch(dev); } else continue; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0113207967d9..24a86c64d22e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -47,6 +47,7 @@ #include <drm/intel-gtt.h> #include <drm/drm_legacy.h> /* for struct drm_dma_handle */ #include <drm/drm_gem.h> +#include <drm/drm_auth.h> #include "i915_params.h" #include "i915_reg.h" @@ -61,12 +62,14 @@ #include "i915_gem_gtt.h" #include "i915_gem_render_state.h" +#include "intel_gvt.h" + /* General customization: */ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20160606" +#define DRIVER_DATE "20160620" #undef WARN_ON /* Many gcc seem to no see through this and fall over :( */ @@ -761,7 +764,8 @@ struct intel_csr { func(has_llc) sep \ func(has_snoop) sep \ func(has_ddi) sep \ - func(has_fpga_dbg) + func(has_fpga_dbg) sep \ + func(has_pooled_eu) #define DEFINE_FLAG(name) u8 name:1 #define SEP_SEMICOLON ; @@ -787,6 +791,7 @@ struct intel_device_info { u8 subslice_per_slice; u8 eu_total; u8 eu_per_subslice; + u8 min_eu_in_pool; /* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */ u8 subslice_7eu[3]; u8 has_slice_pg:1; @@ -876,6 +881,10 @@ struct i915_gem_context { int pin_count; bool initialised; } engine[I915_NUM_ENGINES]; + u32 ring_size; + u32 desc_template; + struct atomic_notifier_head status_notifier; + bool execlists_force_single_submission; struct list_head link; @@ -1739,6 +1748,8 @@ struct drm_i915_private { struct i915_virtual_gpu vgpu; + struct intel_gvt gvt; + struct intel_guc guc; struct intel_csr csr; @@ -2717,6 +2728,15 @@ struct drm_i915_cmd_table { #define IS_BXT_REVID(p, since, until) (IS_BROXTON(p) && IS_REVID(p, since, until)) +#define KBL_REVID_A0 0x0 +#define KBL_REVID_B0 0x1 +#define KBL_REVID_C0 0x2 +#define KBL_REVID_D0 0x3 +#define KBL_REVID_E0 0x4 + +#define IS_KBL_REVID(p, since, until) \ + (IS_KABYLAKE(p) && IS_REVID(p, since, until)) + /* * The genX designation typically refers to the render engine, so render * capability related checks should use IS_GEN, while display and other checks @@ -2823,6 +2843,8 @@ struct drm_i915_cmd_table { !IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) && \ !IS_BROXTON(dev)) +#define HAS_POOLED_EU(dev) (INTEL_INFO(dev)->has_pooled_eu) + #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 @@ -2940,6 +2962,12 @@ void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv, u64 intel_uncore_edram_size(struct drm_i915_private *dev_priv); void assert_forcewakes_inactive(struct drm_i915_private *dev_priv); + +static inline bool intel_gvt_active(struct drm_i915_private *dev_priv) +{ + return dev_priv->gvt.initialized; +} + static inline bool intel_vgpu_active(struct drm_i915_private *dev_priv) { return dev_priv->vgpu.active; @@ -3109,6 +3137,23 @@ static inline int __sg_page_count(struct scatterlist *sg) struct page * i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj, int n); +static inline dma_addr_t +i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj, int n) +{ + if (n < obj->get_page.last) { + obj->get_page.sg = obj->pages->sgl; + obj->get_page.last = 0; + } + + while (obj->get_page.last + __sg_page_count(obj->get_page.sg) <= n) { + obj->get_page.last += __sg_page_count(obj->get_page.sg++); + if (unlikely(sg_is_chain(obj->get_page.sg))) + obj->get_page.sg = sg_chain_ptr(obj->get_page.sg); + } + + return sg_dma_address(obj->get_page.sg) + ((n - obj->get_page.last) << PAGE_SHIFT); +} + static inline struct page * i915_gem_object_get_page(struct drm_i915_gem_object *obj, int n) { @@ -3431,6 +3476,8 @@ int i915_switch_context(struct drm_i915_gem_request *req); void i915_gem_context_free(struct kref *ctx_ref); struct drm_i915_gem_object * i915_gem_alloc_context_obj(struct drm_device *dev, size_t size); +struct i915_gem_context * +i915_gem_context_create_gvt(struct drm_device *dev); static inline struct i915_gem_context * i915_gem_context_lookup(struct drm_i915_file_private *file_priv, u32 id) @@ -3619,6 +3666,7 @@ int intel_bios_init(struct drm_i915_private *dev_priv); bool intel_bios_is_valid_vbt(const void *buf, size_t size); bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv); bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin); +bool intel_bios_is_port_present(struct drm_i915_private *dev_priv, enum port port); bool intel_bios_is_port_edp(struct drm_i915_private *dev_priv, enum port port); bool intel_bios_is_port_dp_dual_mode(struct drm_i915_private *dev_priv, enum port port); bool intel_bios_is_dsi_present(struct drm_i915_private *dev_priv, enum port *port); @@ -3673,7 +3721,7 @@ extern void intel_modeset_init_hw(struct drm_device *dev); extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); -extern void intel_connector_unregister(struct intel_connector *); +extern void intel_connector_unregister(struct drm_connector *); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); extern void intel_display_resume(struct drm_device *dev); extern void i915_redisable_vga(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 343d88114f3b..21d0dea57312 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -54,12 +54,33 @@ static bool cpu_cache_is_coherent(struct drm_device *dev, static bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj) { + if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) + return false; + if (!cpu_cache_is_coherent(obj->base.dev, obj->cache_level)) return true; return obj->pin_display; } +static int +insert_mappable_node(struct drm_i915_private *i915, + struct drm_mm_node *node, u32 size) +{ + memset(node, 0, sizeof(*node)); + return drm_mm_insert_node_in_range_generic(&i915->ggtt.base.mm, node, + size, 0, 0, 0, + i915->ggtt.mappable_end, + DRM_MM_SEARCH_DEFAULT, + DRM_MM_CREATE_DEFAULT); +} + +static void +remove_mappable_node(struct drm_mm_node *node) +{ + drm_mm_remove_node(node); +} + /* some bookkeeping */ static void i915_gem_info_add_obj(struct drm_i915_private *dev_priv, size_t size) @@ -409,6 +430,9 @@ i915_gem_dumb_create(struct drm_file *file, /** * Creates a new mm object and returns a handle to it. + * @dev: drm device pointer + * @data: ioctl data blob + * @file: drm file pointer */ int i915_gem_create_ioctl(struct drm_device *dev, void *data, @@ -585,6 +609,142 @@ shmem_pread_slow(struct page *page, int shmem_page_offset, int page_length, return ret ? - EFAULT : 0; } +static inline unsigned long +slow_user_access(struct io_mapping *mapping, + uint64_t page_base, int page_offset, + char __user *user_data, + unsigned long length, bool pwrite) +{ + void __iomem *ioaddr; + void *vaddr; + uint64_t unwritten; + + ioaddr = io_mapping_map_wc(mapping, page_base, PAGE_SIZE); + /* We can use the cpu mem copy function because this is X86. */ + vaddr = (void __force *)ioaddr + page_offset; + if (pwrite) + unwritten = __copy_from_user(vaddr, user_data, length); + else + unwritten = __copy_to_user(user_data, vaddr, length); + + io_mapping_unmap(ioaddr); + return unwritten; +} + +static int +i915_gem_gtt_pread(struct drm_device *dev, + struct drm_i915_gem_object *obj, uint64_t size, + uint64_t data_offset, uint64_t data_ptr) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_ggtt *ggtt = &dev_priv->ggtt; + struct drm_mm_node node; + char __user *user_data; + uint64_t remain; + uint64_t offset; + int ret; + + ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE); + if (ret) { + ret = insert_mappable_node(dev_priv, &node, PAGE_SIZE); + if (ret) + goto out; + + ret = i915_gem_object_get_pages(obj); + if (ret) { + remove_mappable_node(&node); + goto out; + } + + i915_gem_object_pin_pages(obj); + } else { + node.start = i915_gem_obj_ggtt_offset(obj); + node.allocated = false; + ret = i915_gem_object_put_fence(obj); + if (ret) + goto out_unpin; + } + + ret = i915_gem_object_set_to_gtt_domain(obj, false); + if (ret) + goto out_unpin; + + user_data = u64_to_user_ptr(data_ptr); + remain = size; + offset = data_offset; + + mutex_unlock(&dev->struct_mutex); + if (likely(!i915.prefault_disable)) { + ret = fault_in_multipages_writeable(user_data, remain); + if (ret) { + mutex_lock(&dev->struct_mutex); + goto out_unpin; + } + } + + while (remain > 0) { + /* Operation in this page + * + * page_base = page offset within aperture + * page_offset = offset within page + * page_length = bytes to copy for this page + */ + u32 page_base = node.start; + unsigned page_offset = offset_in_page(offset); + unsigned page_length = PAGE_SIZE - page_offset; + page_length = remain < page_length ? remain : page_length; + if (node.allocated) { + wmb(); + ggtt->base.insert_page(&ggtt->base, + i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT), + node.start, + I915_CACHE_NONE, 0); + wmb(); + } else { + page_base += offset & PAGE_MASK; + } + /* This is a slow read/write as it tries to read from + * and write to user memory which may result into page + * faults, and so we cannot perform this under struct_mutex. + */ + if (slow_user_access(ggtt->mappable, page_base, + page_offset, user_data, + page_length, false)) { + ret = -EFAULT; + break; + } + + remain -= page_length; + user_data += page_length; + offset += page_length; + } + + mutex_lock(&dev->struct_mutex); + if (ret == 0 && (obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) { + /* The user has modified the object whilst we tried + * reading from it, and we now have no idea what domain + * the pages should be in. As we have just been touching + * them directly, flush everything back to the GTT + * domain. + */ + ret = i915_gem_object_set_to_gtt_domain(obj, false); + } + +out_unpin: + if (node.allocated) { + wmb(); + ggtt->base.clear_range(&ggtt->base, + node.start, node.size, + true); + i915_gem_object_unpin_pages(obj); + remove_mappable_node(&node); + } else { + i915_gem_object_ggtt_unpin(obj); + } +out: + return ret; +} + static int i915_gem_shmem_pread(struct drm_device *dev, struct drm_i915_gem_object *obj, @@ -600,6 +760,9 @@ i915_gem_shmem_pread(struct drm_device *dev, int needs_clflush = 0; struct sg_page_iter sg_iter; + if (!obj->base.filp) + return -ENODEV; + user_data = u64_to_user_ptr(args->data_ptr); remain = args->size; @@ -672,6 +835,9 @@ out: /** * Reads data from the object referenced by handle. + * @dev: drm device pointer + * @data: ioctl data blob + * @file: drm file pointer * * On error, the contents of *data are undefined. */ @@ -708,18 +874,15 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, goto out; } - /* prime objects have no backing filp to GEM pread/pwrite - * pages from. - */ - if (!obj->base.filp) { - ret = -EINVAL; - goto out; - } - trace_i915_gem_object_pread(obj, args->offset, args->size); ret = i915_gem_shmem_pread(dev, obj, args, file); + /* pread for non shmem backed objects */ + if (ret == -EFAULT || ret == -ENODEV) + ret = i915_gem_gtt_pread(dev, obj, args->size, + args->offset, args->data_ptr); + out: drm_gem_object_unreference(&obj->base); unlock: @@ -753,60 +916,99 @@ fast_user_write(struct io_mapping *mapping, /** * This is the fast pwrite path, where we copy the data directly from the * user into the GTT, uncached. + * @dev: drm device pointer + * @obj: i915 gem object + * @args: pwrite arguments structure + * @file: drm file pointer */ static int -i915_gem_gtt_pwrite_fast(struct drm_device *dev, +i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915, struct drm_i915_gem_object *obj, struct drm_i915_gem_pwrite *args, struct drm_file *file) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - ssize_t remain; - loff_t offset, page_base; + struct i915_ggtt *ggtt = &i915->ggtt; + struct drm_device *dev = obj->base.dev; + struct drm_mm_node node; + uint64_t remain, offset; char __user *user_data; - int page_offset, page_length, ret; + int ret; + bool hit_slow_path = false; + + if (obj->tiling_mode != I915_TILING_NONE) + return -EFAULT; ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE | PIN_NONBLOCK); - if (ret) - goto out; + if (ret) { + ret = insert_mappable_node(i915, &node, PAGE_SIZE); + if (ret) + goto out; + + ret = i915_gem_object_get_pages(obj); + if (ret) { + remove_mappable_node(&node); + goto out; + } + + i915_gem_object_pin_pages(obj); + } else { + node.start = i915_gem_obj_ggtt_offset(obj); + node.allocated = false; + ret = i915_gem_object_put_fence(obj); + if (ret) + goto out_unpin; + } ret = i915_gem_object_set_to_gtt_domain(obj, true); if (ret) goto out_unpin; - ret = i915_gem_object_put_fence(obj); - if (ret) - goto out_unpin; + intel_fb_obj_invalidate(obj, ORIGIN_GTT); + obj->dirty = true; user_data = u64_to_user_ptr(args->data_ptr); + offset = args->offset; remain = args->size; - - offset = i915_gem_obj_ggtt_offset(obj) + args->offset; - - intel_fb_obj_invalidate(obj, ORIGIN_GTT); - - while (remain > 0) { + while (remain) { /* Operation in this page * * page_base = page offset within aperture * page_offset = offset within page * page_length = bytes to copy for this page */ - page_base = offset & PAGE_MASK; - page_offset = offset_in_page(offset); - page_length = remain; - if ((page_offset + remain) > PAGE_SIZE) - page_length = PAGE_SIZE - page_offset; - + u32 page_base = node.start; + unsigned page_offset = offset_in_page(offset); + unsigned page_length = PAGE_SIZE - page_offset; + page_length = remain < page_length ? remain : page_length; + if (node.allocated) { + wmb(); /* flush the write before we modify the GGTT */ + ggtt->base.insert_page(&ggtt->base, + i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT), + node.start, I915_CACHE_NONE, 0); + wmb(); /* flush modifications to the GGTT (insert_page) */ + } else { + page_base += offset & PAGE_MASK; + } /* If we get a fault while copying data, then (presumably) our * source page isn't available. Return the error and we'll * retry in the slow path. + * If the object is non-shmem backed, we retry again with the + * path that handles page fault. */ if (fast_user_write(ggtt->mappable, page_base, page_offset, user_data, page_length)) { - ret = -EFAULT; - goto out_flush; + hit_slow_path = true; + mutex_unlock(&dev->struct_mutex); + if (slow_user_access(ggtt->mappable, + page_base, + page_offset, user_data, + page_length, true)) { + ret = -EFAULT; + mutex_lock(&dev->struct_mutex); + goto out_flush; + } + + mutex_lock(&dev->struct_mutex); } remain -= page_length; @@ -815,9 +1017,31 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, } out_flush: + if (hit_slow_path) { + if (ret == 0 && + (obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) { + /* The user has modified the object whilst we tried + * reading from it, and we now have no idea what domain + * the pages should be in. As we have just been touching + * them directly, flush everything back to the GTT + * domain. + */ + ret = i915_gem_object_set_to_gtt_domain(obj, false); + } + } + intel_fb_obj_flush(obj, false, ORIGIN_GTT); out_unpin: - i915_gem_object_ggtt_unpin(obj); + if (node.allocated) { + wmb(); + ggtt->base.clear_range(&ggtt->base, + node.start, node.size, + true); + i915_gem_object_unpin_pages(obj); + remove_mappable_node(&node); + } else { + i915_gem_object_ggtt_unpin(obj); + } out: return ret; } @@ -1016,6 +1240,9 @@ out: /** * Writes data to the object referenced by handle. + * @dev: drm device + * @data: ioctl data blob + * @file: drm file * * On error, the contents of the buffer that were to be modified are undefined. */ @@ -1062,14 +1289,6 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, goto out; } - /* prime objects have no backing filp to GEM pread/pwrite - * pages from. - */ - if (!obj->base.filp) { - ret = -EINVAL; - goto out; - } - trace_i915_gem_object_pwrite(obj, args->offset, args->size); ret = -EFAULT; @@ -1079,20 +1298,20 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, * pread/pwrite currently are reading and writing from the CPU * perspective, requiring manual detiling by the client. */ - if (obj->tiling_mode == I915_TILING_NONE && - obj->base.write_domain != I915_GEM_DOMAIN_CPU && - cpu_write_needs_clflush(obj)) { - ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file); + if (!obj->base.filp || cpu_write_needs_clflush(obj)) { + ret = i915_gem_gtt_pwrite_fast(dev_priv, obj, args, file); /* Note that the gtt paths might fail with non-page-backed user * pointers (e.g. gtt mappings when moving data between * textures). Fallback to the shmem path in that case. */ } - if (ret == -EFAULT || ret == -ENOSPC) { + if (ret == -EFAULT) { if (obj->phys_handle) ret = i915_gem_phys_pwrite(obj, args, file); - else + else if (obj->base.filp) ret = i915_gem_shmem_pwrite(dev, obj, args, file); + else + ret = -ENODEV; } out: @@ -1213,6 +1432,7 @@ static int __i915_spin_request(struct drm_i915_gem_request *req, int state) * @req: duh! * @interruptible: do an interruptible wait (normally yes) * @timeout: in - how long to wait (NULL forever); out - how much time remaining + * @rps: RPS client * * Note: It is of utmost importance that the passed in seqno and reset_counter * values have been read by the caller in an smp safe manner. Where read-side @@ -1446,6 +1666,7 @@ __i915_gem_request_retire__upto(struct drm_i915_gem_request *req) /** * Waits for a request to be signaled, and cleans up the * request and object lists appropriately for that event. + * @req: request to wait on */ int i915_wait_request(struct drm_i915_gem_request *req) @@ -1472,6 +1693,8 @@ i915_wait_request(struct drm_i915_gem_request *req) /** * Ensures that all rendering to the object has completed and the object is * safe to unbind from the GTT or access from the CPU. + * @obj: i915 gem object + * @readonly: waiting for read access or write */ int i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, @@ -1589,6 +1812,9 @@ static struct intel_rps_client *to_rps_client(struct drm_file *file) /** * Called when user space prepares to use an object with the CPU, either * through the mmap ioctl's mapping or a GTT mapping. + * @dev: drm device + * @data: ioctl data blob + * @file: drm file */ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, @@ -1652,6 +1878,9 @@ unlock: /** * Called when user space has done writes to this buffer + * @dev: drm device + * @data: ioctl data blob + * @file: drm file */ int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, @@ -1682,8 +1911,11 @@ unlock: } /** - * Maps the contents of an object, returning the address it is mapped - * into. + * i915_gem_mmap_ioctl - Maps the contents of an object, returning the address + * it is mapped to. + * @dev: drm device + * @data: ioctl data blob + * @file: drm file * * While the mapping holds a reference on the contents of the object, it doesn't * imply a ref on the object itself. @@ -2001,7 +2233,10 @@ i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) /** * i915_gem_get_gtt_alignment - return required GTT alignment for an object - * @obj: object to check + * @dev: drm device + * @size: object size + * @tiling_mode: tiling mode + * @fenced: is fenced alignemned required or not * * Return the required GTT alignment for an object, taking into account * potential fence register mapping. @@ -2951,6 +3186,7 @@ void i915_gem_reset(struct drm_device *dev) /** * This function clears the request list as sequence numbers are passed. + * @engine: engine to retire requests on */ void i915_gem_retire_requests_ring(struct intel_engine_cs *engine) @@ -3074,6 +3310,7 @@ i915_gem_idle_work_handler(struct work_struct *work) * Ensures that an object will eventually get non-busy by flushing any required * write domains, emitting any outstanding lazy request and retiring and * completed requests. + * @obj: object to flush */ static int i915_gem_object_flush_active(struct drm_i915_gem_object *obj) @@ -3099,7 +3336,9 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) /** * i915_gem_wait_ioctl - implements DRM_IOCTL_I915_GEM_WAIT - * @DRM_IOCTL_ARGS: standard ioctl arguments + * @dev: drm device pointer + * @data: ioctl data blob + * @file: drm file pointer * * Returns 0 if successful, else an error is returned with the remaining time in * the timeout parameter. @@ -3489,6 +3728,11 @@ static bool i915_gem_valid_gtt_space(struct i915_vma *vma, /** * Finds free space in the GTT aperture and binds the object or a view of it * there. + * @obj: object to bind + * @vm: address space to bind into + * @ggtt_view: global gtt view if applicable + * @alignment: requested alignment + * @flags: mask of PIN_* flags to use */ static struct i915_vma * i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, @@ -3746,6 +3990,8 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj) /** * Moves a single object to the GTT read, and possibly write domain. + * @obj: object to act on + * @write: ask for write access or read only * * This function returns when the move is complete, including waiting on * flushes to occur. @@ -3817,6 +4063,8 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) /** * Changes the cache-level of an object across all VMA. + * @obj: object to act on + * @cache_level: new cache level to set for the object * * After this function returns, the object will be in the new cache-level * across all GTT and the contents of the backing storage will be coherent, @@ -3926,9 +4174,7 @@ out: * object is now coherent at its new cache level (with respect * to the access domain). */ - if (obj->cache_dirty && - obj->base.write_domain != I915_GEM_DOMAIN_CPU && - cpu_write_needs_clflush(obj)) { + if (obj->cache_dirty && cpu_write_needs_clflush(obj)) { if (i915_gem_clflush_object(obj, true)) i915_gem_chipset_flush(to_i915(obj->base.dev)); } @@ -4098,6 +4344,8 @@ i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj, /** * Moves a single object to the CPU read, and possibly write domain. + * @obj: object to act on + * @write: requesting write or read-only access * * This function returns when the move is complete, including waiting on * flushes to occur. @@ -4886,11 +5134,9 @@ i915_gem_init_hw(struct drm_device *dev) intel_mocs_init_l3cc_table(dev); /* We can't enable contexts until all firmware is loaded */ - if (HAS_GUC(dev)) { - ret = intel_guc_setup(dev); - if (ret) - goto out; - } + ret = intel_guc_setup(dev); + if (ret) + goto out; /* * Increment the next seqno by 0x100 so we have a visible break diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a3b11aac23a4..30d9b4fd30f3 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -295,6 +295,10 @@ __create_hw_context(struct drm_device *dev, ctx->remap_slice = ALL_L3_SLICES(dev_priv); ctx->hang_stats.ban_period_seconds = DRM_I915_CTX_BAN_PERIOD; + ctx->ring_size = 4 * PAGE_SIZE; + ctx->desc_template = GEN8_CTX_ADDRESSING_MODE(dev_priv) << + GEN8_CTX_ADDRESSING_MODE_SHIFT; + ATOMIC_INIT_NOTIFIER_HEAD(&ctx->status_notifier); return ctx; @@ -339,6 +343,40 @@ i915_gem_create_context(struct drm_device *dev, return ctx; } +/** + * i915_gem_context_create_gvt - create a GVT GEM context + * @dev: drm device * + * + * This function is used to create a GVT specific GEM context. + * + * Returns: + * pointer to i915_gem_context on success, error pointer if failed + * + */ +struct i915_gem_context * +i915_gem_context_create_gvt(struct drm_device *dev) +{ + struct i915_gem_context *ctx; + int ret; + + if (!IS_ENABLED(CONFIG_DRM_I915_GVT)) + return ERR_PTR(-ENODEV); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ERR_PTR(ret); + + ctx = i915_gem_create_context(dev, NULL); + if (IS_ERR(ctx)) + goto out; + + ctx->execlists_force_single_submission = true; + ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */ +out: + mutex_unlock(&dev->struct_mutex); + return ctx; +} + static void i915_gem_context_unpin(struct i915_gem_context *ctx, struct intel_engine_cs *engine) { diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.h b/drivers/gpu/drm/i915/i915_gem_dmabuf.h new file mode 100644 index 000000000000..91315557e421 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.h @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _I915_GEM_DMABUF_H_ +#define _I915_GEM_DMABUF_H_ + +#include <linux/dma-buf.h> + +static inline struct reservation_object * +i915_gem_object_get_dmabuf_resv(struct drm_i915_gem_object *obj) +{ + struct dma_buf *dma_buf; + + if (obj->base.dma_buf) + dma_buf = obj->base.dma_buf; + else if (obj->base.import_attach) + dma_buf = obj->base.import_attach->dmabuf; + else + return NULL; + + return dma_buf->resv; +} + +#endif diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 8097698b9622..7941f1fe9cd2 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1446,7 +1446,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, dispatch_flags = 0; if (args->flags & I915_EXEC_SECURE) { - if (!file->is_master || !capable(CAP_SYS_ADMIN)) + if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN)) return -EPERM; dispatch_flags |= I915_DISPATCH_SECURE; @@ -1553,7 +1553,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, batch_obj, args->batch_start_offset, args->batch_len, - file->is_master); + drm_is_current_master(file)); if (IS_ERR(parsed_batch_obj)) { ret = PTR_ERR(parsed_batch_obj); goto err; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 46684779d4d6..5890017b9832 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2355,6 +2355,28 @@ static void gen8_set_pte(void __iomem *addr, gen8_pte_t pte) #endif } +static void gen8_ggtt_insert_page(struct i915_address_space *vm, + dma_addr_t addr, + uint64_t offset, + enum i915_cache_level level, + u32 unused) +{ + struct drm_i915_private *dev_priv = to_i915(vm->dev); + gen8_pte_t __iomem *pte = + (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + + (offset >> PAGE_SHIFT); + int rpm_atomic_seq; + + rpm_atomic_seq = assert_rpm_atomic_begin(dev_priv); + + gen8_set_pte(pte, gen8_pte_encode(addr, level, true)); + + I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); + POSTING_READ(GFX_FLSH_CNTL_GEN6); + + assert_rpm_atomic_end(dev_priv, rpm_atomic_seq); +} + static void gen8_ggtt_insert_entries(struct i915_address_space *vm, struct sg_table *st, uint64_t start, @@ -2424,6 +2446,28 @@ static void gen8_ggtt_insert_entries__BKL(struct i915_address_space *vm, stop_machine(gen8_ggtt_insert_entries__cb, &arg, NULL); } +static void gen6_ggtt_insert_page(struct i915_address_space *vm, + dma_addr_t addr, + uint64_t offset, + enum i915_cache_level level, + u32 flags) +{ + struct drm_i915_private *dev_priv = to_i915(vm->dev); + gen6_pte_t __iomem *pte = + (gen6_pte_t __iomem *)dev_priv->ggtt.gsm + + (offset >> PAGE_SHIFT); + int rpm_atomic_seq; + + rpm_atomic_seq = assert_rpm_atomic_begin(dev_priv); + + iowrite32(vm->pte_encode(addr, level, true, flags), pte); + + I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); + POSTING_READ(GFX_FLSH_CNTL_GEN6); + + assert_rpm_atomic_end(dev_priv, rpm_atomic_seq); +} + /* * Binds an object into the global gtt with the specified cache level. The object * will be accessible to the GPU via commands whose operands reference offsets @@ -2543,6 +2587,24 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm, assert_rpm_atomic_end(dev_priv, rpm_atomic_seq); } +static void i915_ggtt_insert_page(struct i915_address_space *vm, + dma_addr_t addr, + uint64_t offset, + enum i915_cache_level cache_level, + u32 unused) +{ + struct drm_i915_private *dev_priv = to_i915(vm->dev); + unsigned int flags = (cache_level == I915_CACHE_NONE) ? + AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY; + int rpm_atomic_seq; + + rpm_atomic_seq = assert_rpm_atomic_begin(dev_priv); + + intel_gtt_insert_page(addr, offset >> PAGE_SHIFT, flags); + + assert_rpm_atomic_end(dev_priv, rpm_atomic_seq); +} + static void i915_ggtt_insert_entries(struct i915_address_space *vm, struct sg_table *pages, uint64_t start, @@ -2732,11 +2794,9 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, i915_address_space_init(&ggtt->base, dev_priv); ggtt->base.total += PAGE_SIZE; - if (intel_vgpu_active(dev_priv)) { - ret = intel_vgt_balloon(dev); - if (ret) - return ret; - } + ret = intel_vgt_balloon(dev_priv); + if (ret) + return ret; if (!HAS_LLC(dev)) ggtt->base.mm.color_adjust = i915_gtt_color_adjust; @@ -2836,8 +2896,7 @@ void i915_ggtt_cleanup_hw(struct drm_device *dev) i915_gem_cleanup_stolen(dev); if (drm_mm_initialized(&ggtt->base.mm)) { - if (intel_vgpu_active(dev_priv)) - intel_vgt_deballoon(); + intel_vgt_deballoon(dev_priv); drm_mm_takedown(&ggtt->base.mm); list_del(&ggtt->base.global_link); @@ -3076,7 +3135,7 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) ggtt->base.bind_vma = ggtt_bind_vma; ggtt->base.unbind_vma = ggtt_unbind_vma; - + ggtt->base.insert_page = gen8_ggtt_insert_page; ggtt->base.clear_range = nop_clear_range; if (!USES_FULL_PPGTT(dev_priv)) ggtt->base.clear_range = gen8_ggtt_clear_range; @@ -3116,6 +3175,7 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt) ret = ggtt_probe_common(dev, ggtt->size); ggtt->base.clear_range = gen6_ggtt_clear_range; + ggtt->base.insert_page = gen6_ggtt_insert_page; ggtt->base.insert_entries = gen6_ggtt_insert_entries; ggtt->base.bind_vma = ggtt_bind_vma; ggtt->base.unbind_vma = ggtt_unbind_vma; @@ -3147,6 +3207,7 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt) &ggtt->mappable_base, &ggtt->mappable_end); ggtt->do_idle_maps = needs_idle_maps(dev_priv->dev); + ggtt->base.insert_page = i915_ggtt_insert_page; ggtt->base.insert_entries = i915_ggtt_insert_entries; ggtt->base.clear_range = i915_ggtt_clear_range; ggtt->base.bind_vma = ggtt_bind_vma; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 62be77cac5cd..163b564fb87d 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -319,6 +319,11 @@ struct i915_address_space { uint64_t start, uint64_t length, bool use_scratch); + void (*insert_page)(struct i915_address_space *vm, + dma_addr_t addr, + uint64_t offset, + enum i915_cache_level cache_level, + u32 flags); void (*insert_entries)(struct i915_address_space *vm, struct sg_table *st, uint64_t start, diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index 7c93327b70fe..b7c1b5fb61ea 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -94,6 +94,7 @@ free_gem: static int render_state_setup(struct render_state *so) { + struct drm_device *dev = so->obj->base.dev; const struct intel_renderstate_rodata *rodata = so->rodata; unsigned int i = 0, reloc_index = 0; struct page *page; @@ -135,6 +136,33 @@ static int render_state_setup(struct render_state *so) so->aux_batch_offset = i * sizeof(u32); + if (HAS_POOLED_EU(dev)) { + /* + * We always program 3x6 pool config but depending upon which + * subslice is disabled HW drops down to appropriate config + * shown below. + * + * In the below table 2x6 config always refers to + * fused-down version, native 2x6 is not available and can + * be ignored + * + * SNo subslices config eu pool configuration + * ----------------------------------------------------------- + * 1 3 subslices enabled (3x6) - 0x00777000 (9+9) + * 2 ss0 disabled (2x6) - 0x00777000 (3+9) + * 3 ss1 disabled (2x6) - 0x00770000 (6+6) + * 4 ss2 disabled (2x6) - 0x00007000 (9+3) + */ + u32 eu_pool_config = 0x00777000; + + OUT_BATCH(d, i, GEN9_MEDIA_POOL_STATE); + OUT_BATCH(d, i, GEN9_MEDIA_POOL_ENABLE); + OUT_BATCH(d, i, eu_pool_config); + OUT_BATCH(d, i, 0); + OUT_BATCH(d, i, 0); + OUT_BATCH(d, i, 0); + } + OUT_BATCH(d, i, MI_BATCH_BUFFER_END); so->aux_batch_size = (i * sizeof(u32)) - so->aux_batch_offset; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index f9253f2b7ba0..e9cd82290408 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -55,8 +55,10 @@ int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv, return -ENODEV; /* See the comment at the drm_mm_init() call for more about this check. - * WaSkipStolenMemoryFirstPage:bdw,chv (incomplete) */ - if (IS_GEN8(dev_priv) && start < 4096) + * WaSkipStolenMemoryFirstPage:bdw,chv,kbl (incomplete) + */ + if (start < 4096 && (IS_GEN8(dev_priv) || + IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0))) start = 4096; mutex_lock(&dev_priv->mm.stolen_lock); diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index ac72451c571c..22a55ac4e51c 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -174,94 +174,88 @@ static int host2guc_sample_forcewake(struct intel_guc *guc, * client object which contains the page being used for the doorbell */ -static void guc_init_doorbell(struct intel_guc *guc, - struct i915_guc_client *client) +static int guc_update_doorbell_id(struct intel_guc *guc, + struct i915_guc_client *client, + u16 new_id) { + struct sg_table *sg = guc->ctx_pool_obj->pages; + void *doorbell_bitmap = guc->doorbell_bitmap; struct guc_doorbell_info *doorbell; + struct guc_context_desc desc; + size_t len; doorbell = client->client_base + client->doorbell_offset; - doorbell->db_status = GUC_DOORBELL_ENABLED; - doorbell->cookie = 0; -} - -static int guc_ring_doorbell(struct i915_guc_client *gc) -{ - struct guc_process_desc *desc; - union guc_doorbell_qw db_cmp, db_exc, db_ret; - union guc_doorbell_qw *db; - int attempt = 2, ret = -EAGAIN; - - desc = gc->client_base + gc->proc_desc_offset; - - /* Update the tail so it is visible to GuC */ - desc->tail = gc->wq_tail; - - /* current cookie */ - db_cmp.db_status = GUC_DOORBELL_ENABLED; - db_cmp.cookie = gc->cookie; - - /* cookie to be updated */ - db_exc.db_status = GUC_DOORBELL_ENABLED; - db_exc.cookie = gc->cookie + 1; - if (db_exc.cookie == 0) - db_exc.cookie = 1; - - /* pointer of current doorbell cacheline */ - db = gc->client_base + gc->doorbell_offset; - - while (attempt--) { - /* lets ring the doorbell */ - db_ret.value_qw = atomic64_cmpxchg((atomic64_t *)db, - db_cmp.value_qw, db_exc.value_qw); - - /* if the exchange was successfully executed */ - if (db_ret.value_qw == db_cmp.value_qw) { - /* db was successfully rung */ - gc->cookie = db_exc.cookie; - ret = 0; - break; - } + if (client->doorbell_id != GUC_INVALID_DOORBELL_ID && + test_bit(client->doorbell_id, doorbell_bitmap)) { + /* Deactivate the old doorbell */ + doorbell->db_status = GUC_DOORBELL_DISABLED; + (void)host2guc_release_doorbell(guc, client); + __clear_bit(client->doorbell_id, doorbell_bitmap); + } - /* XXX: doorbell was lost and need to acquire it again */ - if (db_ret.db_status == GUC_DOORBELL_DISABLED) - break; + /* Update the GuC's idea of the doorbell ID */ + len = sg_pcopy_to_buffer(sg->sgl, sg->nents, &desc, sizeof(desc), + sizeof(desc) * client->ctx_index); + if (len != sizeof(desc)) + return -EFAULT; + desc.db_id = new_id; + len = sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc), + sizeof(desc) * client->ctx_index); + if (len != sizeof(desc)) + return -EFAULT; - DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n", - db_cmp.cookie, db_ret.cookie); + client->doorbell_id = new_id; + if (new_id == GUC_INVALID_DOORBELL_ID) + return 0; - /* update the cookie to newly read cookie from GuC */ - db_cmp.cookie = db_ret.cookie; - db_exc.cookie = db_ret.cookie + 1; - if (db_exc.cookie == 0) - db_exc.cookie = 1; - } + /* Activate the new doorbell */ + __set_bit(new_id, doorbell_bitmap); + doorbell->cookie = 0; + doorbell->db_status = GUC_DOORBELL_ENABLED; + return host2guc_allocate_doorbell(guc, client); +} - return ret; +static int guc_init_doorbell(struct intel_guc *guc, + struct i915_guc_client *client, + uint16_t db_id) +{ + return guc_update_doorbell_id(guc, client, db_id); } static void guc_disable_doorbell(struct intel_guc *guc, struct i915_guc_client *client) { - struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct guc_doorbell_info *doorbell; - i915_reg_t drbreg = GEN8_DRBREGL(client->doorbell_id); - int value; + (void)guc_update_doorbell_id(guc, client, GUC_INVALID_DOORBELL_ID); - doorbell = client->client_base + client->doorbell_offset; - - doorbell->db_status = GUC_DOORBELL_DISABLED; + /* XXX: wait for any interrupts */ + /* XXX: wait for workqueue to drain */ +} - I915_WRITE(drbreg, I915_READ(drbreg) & ~GEN8_DRB_VALID); +static uint16_t +select_doorbell_register(struct intel_guc *guc, uint32_t priority) +{ + /* + * The bitmap tracks which doorbell registers are currently in use. + * It is split into two halves; the first half is used for normal + * priority contexts, the second half for high-priority ones. + * Note that logically higher priorities are numerically less than + * normal ones, so the test below means "is it high-priority?" + */ + const bool hi_pri = (priority <= GUC_CTX_PRIORITY_HIGH); + const uint16_t half = GUC_MAX_DOORBELLS / 2; + const uint16_t start = hi_pri ? half : 0; + const uint16_t end = start + half; + uint16_t id; - value = I915_READ(drbreg); - WARN_ON((value & GEN8_DRB_VALID) != 0); + id = find_next_zero_bit(guc->doorbell_bitmap, end, start); + if (id == end) + id = GUC_INVALID_DOORBELL_ID; - I915_WRITE(GEN8_DRBREGU(client->doorbell_id), 0); - I915_WRITE(drbreg, 0); + DRM_DEBUG_DRIVER("assigned %s priority doorbell id 0x%x\n", + hi_pri ? "high" : "normal", id); - /* XXX: wait for any interrupts */ - /* XXX: wait for workqueue to drain */ + return id; } /* @@ -288,37 +282,6 @@ static uint32_t select_doorbell_cacheline(struct intel_guc *guc) return offset; } -static uint16_t assign_doorbell(struct intel_guc *guc, uint32_t priority) -{ - /* - * The bitmap is split into two halves; the first half is used for - * normal priority contexts, the second half for high-priority ones. - * Note that logically higher priorities are numerically less than - * normal ones, so the test below means "is it high-priority?" - */ - const bool hi_pri = (priority <= GUC_CTX_PRIORITY_HIGH); - const uint16_t half = GUC_MAX_DOORBELLS / 2; - const uint16_t start = hi_pri ? half : 0; - const uint16_t end = start + half; - uint16_t id; - - id = find_next_zero_bit(guc->doorbell_bitmap, end, start); - if (id == end) - id = GUC_INVALID_DOORBELL_ID; - else - bitmap_set(guc->doorbell_bitmap, id, 1); - - DRM_DEBUG_DRIVER("assigned %s priority doorbell id 0x%x\n", - hi_pri ? "high" : "normal", id); - - return id; -} - -static void release_doorbell(struct intel_guc *guc, uint16_t id) -{ - bitmap_clear(guc->doorbell_bitmap, id, 1); -} - /* * Initialise the process descriptor shared with the GuC firmware. */ @@ -543,6 +506,61 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc, kunmap_atomic(base); } +static int guc_ring_doorbell(struct i915_guc_client *gc) +{ + struct guc_process_desc *desc; + union guc_doorbell_qw db_cmp, db_exc, db_ret; + union guc_doorbell_qw *db; + int attempt = 2, ret = -EAGAIN; + + desc = gc->client_base + gc->proc_desc_offset; + + /* Update the tail so it is visible to GuC */ + desc->tail = gc->wq_tail; + + /* current cookie */ + db_cmp.db_status = GUC_DOORBELL_ENABLED; + db_cmp.cookie = gc->cookie; + + /* cookie to be updated */ + db_exc.db_status = GUC_DOORBELL_ENABLED; + db_exc.cookie = gc->cookie + 1; + if (db_exc.cookie == 0) + db_exc.cookie = 1; + + /* pointer of current doorbell cacheline */ + db = gc->client_base + gc->doorbell_offset; + + while (attempt--) { + /* lets ring the doorbell */ + db_ret.value_qw = atomic64_cmpxchg((atomic64_t *)db, + db_cmp.value_qw, db_exc.value_qw); + + /* if the exchange was successfully executed */ + if (db_ret.value_qw == db_cmp.value_qw) { + /* db was successfully rung */ + gc->cookie = db_exc.cookie; + ret = 0; + break; + } + + /* XXX: doorbell was lost and need to acquire it again */ + if (db_ret.db_status == GUC_DOORBELL_DISABLED) + break; + + DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n", + db_cmp.cookie, db_ret.cookie); + + /* update the cookie to newly read cookie from GuC */ + db_cmp.cookie = db_ret.cookie; + db_exc.cookie = db_ret.cookie + 1; + if (db_exc.cookie == 0) + db_exc.cookie = 1; + } + + return ret; +} + /** * i915_guc_submit() - Submit commands through GuC * @rq: request associated with the commands @@ -591,7 +609,7 @@ int i915_guc_submit(struct drm_i915_gem_request *rq) /** * gem_allocate_guc_obj() - Allocate gem object for GuC usage - * @dev: drm device + * @dev_priv: driver private data structure * @size: size of object * * This is a wrapper to create a gem obj. In order to use it inside GuC, the @@ -600,13 +618,12 @@ int i915_guc_submit(struct drm_i915_gem_request *rq) * * Return: A drm_i915_gem_object if successful, otherwise NULL. */ -static struct drm_i915_gem_object *gem_allocate_guc_obj(struct drm_device *dev, - u32 size) +static struct drm_i915_gem_object * +gem_allocate_guc_obj(struct drm_i915_private *dev_priv, u32 size) { - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; - obj = i915_gem_object_create(dev, size); + obj = i915_gem_object_create(dev_priv->dev, size); if (IS_ERR(obj)) return NULL; @@ -642,10 +659,10 @@ static void gem_release_guc_obj(struct drm_i915_gem_object *obj) drm_gem_object_unreference(&obj->base); } -static void guc_client_free(struct drm_device *dev, - struct i915_guc_client *client) +static void +guc_client_free(struct drm_i915_private *dev_priv, + struct i915_guc_client *client) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; if (!client) @@ -658,17 +675,10 @@ static void guc_client_free(struct drm_device *dev, if (client->client_base) { /* - * If we got as far as setting up a doorbell, make sure - * we shut it down before unmapping & deallocating the - * memory. So first disable the doorbell, then tell the - * GuC that we've finished with it, finally deallocate - * it in our bitmap + * If we got as far as setting up a doorbell, make sure we + * shut it down before unmapping & deallocating the memory. */ - if (client->doorbell_id != GUC_INVALID_DOORBELL_ID) { - guc_disable_doorbell(guc, client); - host2guc_release_doorbell(guc, client); - release_doorbell(guc, client->doorbell_id); - } + guc_disable_doorbell(guc, client); kunmap(kmap_to_page(client->client_base)); } @@ -683,9 +693,51 @@ static void guc_client_free(struct drm_device *dev, kfree(client); } +/* + * Borrow the first client to set up & tear down every doorbell + * in turn, to ensure that all doorbell h/w is (re)initialised. + */ +static void guc_init_doorbell_hw(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct i915_guc_client *client = guc->execbuf_client; + uint16_t db_id, i; + int err; + + db_id = client->doorbell_id; + + for (i = 0; i < GUC_MAX_DOORBELLS; ++i) { + i915_reg_t drbreg = GEN8_DRBREGL(i); + u32 value = I915_READ(drbreg); + + err = guc_update_doorbell_id(guc, client, i); + + /* Report update failure or unexpectedly active doorbell */ + if (err || (i != db_id && (value & GUC_DOORBELL_ENABLED))) + DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) was 0x%x, err %d\n", + i, drbreg.reg, value, err); + } + + /* Restore to original value */ + err = guc_update_doorbell_id(guc, client, db_id); + if (err) + DRM_ERROR("Failed to restore doorbell to %d, err %d\n", + db_id, err); + + for (i = 0; i < GUC_MAX_DOORBELLS; ++i) { + i915_reg_t drbreg = GEN8_DRBREGL(i); + u32 value = I915_READ(drbreg); + + if (i != db_id && (value & GUC_DOORBELL_ENABLED)) + DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) finally 0x%x\n", + i, drbreg.reg, value); + + } +} + /** * guc_client_alloc() - Allocate an i915_guc_client - * @dev: drm device + * @dev_priv: driver private data structure * @priority: four levels priority _CRITICAL, _HIGH, _NORMAL and _LOW * The kernel client to replace ExecList submission is created with * NORMAL priority. Priority of a client for scheduler can be HIGH, @@ -695,14 +747,15 @@ static void guc_client_free(struct drm_device *dev, * * Return: An i915_guc_client object if success, else NULL. */ -static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, - uint32_t priority, - struct i915_gem_context *ctx) +static struct i915_guc_client * +guc_client_alloc(struct drm_i915_private *dev_priv, + uint32_t priority, + struct i915_gem_context *ctx) { struct i915_guc_client *client; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; struct drm_i915_gem_object *obj; + uint16_t db_id; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) @@ -721,7 +774,7 @@ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, } /* The first page is doorbell/proc_desc. Two followed pages are wq. */ - obj = gem_allocate_guc_obj(dev, GUC_DB_SIZE + GUC_WQ_SIZE); + obj = gem_allocate_guc_obj(dev_priv, GUC_DB_SIZE + GUC_WQ_SIZE); if (!obj) goto err; @@ -731,6 +784,11 @@ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, client->wq_offset = GUC_DB_SIZE; client->wq_size = GUC_WQ_SIZE; + db_id = select_doorbell_register(guc, client->priority); + if (db_id == GUC_INVALID_DOORBELL_ID) + /* XXX: evict a doorbell instead? */ + goto err; + client->doorbell_offset = select_doorbell_cacheline(guc); /* @@ -743,29 +801,22 @@ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, else client->proc_desc_offset = (GUC_DB_SIZE / 2); - client->doorbell_id = assign_doorbell(guc, client->priority); - if (client->doorbell_id == GUC_INVALID_DOORBELL_ID) - /* XXX: evict a doorbell instead */ - goto err; - guc_init_proc_desc(guc, client); guc_init_ctx_desc(guc, client); - guc_init_doorbell(guc, client); - - /* XXX: Any cache flushes needed? General domain mgmt calls? */ - - if (host2guc_allocate_doorbell(guc, client)) + if (guc_init_doorbell(guc, client, db_id)) goto err; - DRM_DEBUG_DRIVER("new priority %u client %p: ctx_index %u db_id %u\n", - priority, client, client->ctx_index, client->doorbell_id); + DRM_DEBUG_DRIVER("new priority %u client %p: ctx_index %u\n", + priority, client, client->ctx_index); + DRM_DEBUG_DRIVER("doorbell id %u, cacheline offset 0x%x\n", + client->doorbell_id, client->doorbell_offset); return client; err: DRM_ERROR("FAILED to create priority %u GuC client!\n", priority); - guc_client_free(dev, client); + guc_client_free(dev_priv, client); return NULL; } @@ -790,7 +841,7 @@ static void guc_create_log(struct intel_guc *guc) obj = guc->log_obj; if (!obj) { - obj = gem_allocate_guc_obj(dev_priv->dev, size); + obj = gem_allocate_guc_obj(dev_priv, size); if (!obj) { /* logging will be off */ i915.guc_log_level = -1; @@ -850,7 +901,7 @@ static void guc_create_ads(struct intel_guc *guc) obj = guc->ads_obj; if (!obj) { - obj = gem_allocate_guc_obj(dev_priv->dev, PAGE_ALIGN(size)); + obj = gem_allocate_guc_obj(dev_priv, PAGE_ALIGN(size)); if (!obj) return; @@ -904,41 +955,41 @@ static void guc_create_ads(struct intel_guc *guc) * Set up the memory resources to be shared with the GuC. At this point, * we require just one object that can be mapped through the GGTT. */ -int i915_guc_submission_init(struct drm_device *dev) +int i915_guc_submission_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = dev->dev_private; const size_t ctxsize = sizeof(struct guc_context_desc); const size_t poolsize = GUC_MAX_GPU_CONTEXTS * ctxsize; const size_t gemsize = round_up(poolsize, PAGE_SIZE); struct intel_guc *guc = &dev_priv->guc; + /* Wipe bitmap & delete client in case of reinitialisation */ + bitmap_clear(guc->doorbell_bitmap, 0, GUC_MAX_DOORBELLS); + i915_guc_submission_disable(dev_priv); + if (!i915.enable_guc_submission) return 0; /* not enabled */ if (guc->ctx_pool_obj) return 0; /* already allocated */ - guc->ctx_pool_obj = gem_allocate_guc_obj(dev_priv->dev, gemsize); + guc->ctx_pool_obj = gem_allocate_guc_obj(dev_priv, gemsize); if (!guc->ctx_pool_obj) return -ENOMEM; ida_init(&guc->ctx_ids); - guc_create_log(guc); - guc_create_ads(guc); return 0; } -int i915_guc_submission_enable(struct drm_device *dev) +int i915_guc_submission_enable(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; struct i915_guc_client *client; /* client for execbuf submission */ - client = guc_client_alloc(dev, + client = guc_client_alloc(dev_priv, GUC_CTX_PRIORITY_KMD_NORMAL, dev_priv->kernel_context); if (!client) { @@ -947,24 +998,22 @@ int i915_guc_submission_enable(struct drm_device *dev) } guc->execbuf_client = client; - host2guc_sample_forcewake(guc, client); + guc_init_doorbell_hw(guc); return 0; } -void i915_guc_submission_disable(struct drm_device *dev) +void i915_guc_submission_disable(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; - guc_client_free(dev, guc->execbuf_client); + guc_client_free(dev_priv, guc->execbuf_client); guc->execbuf_client = NULL; } -void i915_guc_submission_fini(struct drm_device *dev) +void i915_guc_submission_fini(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; gem_release_guc_obj(dev_priv->guc.ads_obj); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 5c7378374ae6..4378a659d962 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -588,7 +588,7 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, /** * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion - * @dev: drm device + * @dev_priv: i915 device private */ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) { @@ -2517,7 +2517,7 @@ static void i915_error_wake_up(struct drm_i915_private *dev_priv, /** * i915_reset_and_wakeup - do process context error handling work - * @dev: drm device + * @dev_priv: i915 device private * * Fire an error uevent so userspace can see that a hang or error * was detected. @@ -2674,13 +2674,14 @@ static void i915_report_and_clear_eir(struct drm_i915_private *dev_priv) /** * i915_handle_error - handle a gpu error - * @dev: drm device + * @dev_priv: i915 device private * @engine_mask: mask representing engines that are hung * Do some basic checking of register state at error time and * dump it to the syslog. Also call i915_capture_error_state() to make * sure we get a record and make it available in debugfs. Fire a uevent * so userspace knows something bad happened (should trigger collection * of a ring dump etc.). + * @fmt: Error message format string */ void i915_handle_error(struct drm_i915_private *dev_priv, u32 engine_mask, diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 5e18cf9f754d..7effe68d552c 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -54,12 +54,13 @@ struct i915_params i915 __read_mostly = { .verbose_state_checks = 1, .nuclear_pageflip = 0, .edp_vswing = 0, - .enable_guc_loading = 0, - .enable_guc_submission = 0, + .enable_guc_loading = -1, + .enable_guc_submission = -1, .guc_log_level = -1, .enable_dp_mst = true, .inject_load_failure = 0, .enable_dpcd_backlight = false, + .enable_gvt = false, }; module_param_named(modeset, i915.modeset, int, 0400); @@ -202,12 +203,12 @@ MODULE_PARM_DESC(edp_vswing, module_param_named_unsafe(enable_guc_loading, i915.enable_guc_loading, int, 0400); MODULE_PARM_DESC(enable_guc_loading, "Enable GuC firmware loading " - "(-1=auto, 0=never [default], 1=if available, 2=required)"); + "(-1=auto [default], 0=never, 1=if available, 2=required)"); module_param_named_unsafe(enable_guc_submission, i915.enable_guc_submission, int, 0400); MODULE_PARM_DESC(enable_guc_submission, "Enable GuC submission " - "(-1=auto, 0=never [default], 1=if available, 2=required)"); + "(-1=auto [default], 0=never, 1=if available, 2=required)"); module_param_named(guc_log_level, i915.guc_log_level, int, 0400); MODULE_PARM_DESC(guc_log_level, @@ -222,3 +223,7 @@ MODULE_PARM_DESC(inject_load_failure, module_param_named(enable_dpcd_backlight, i915.enable_dpcd_backlight, bool, 0600); MODULE_PARM_DESC(enable_dpcd_backlight, "Enable support for DPCD backlight control (default:false)"); + +module_param_named(enable_gvt, i915.enable_gvt, bool, 0600); +MODULE_PARM_DESC(enable_gvt, + "Enable support for Intel GVT-g graphics virtualization host support(default:false)"); diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 1323261a0cdd..0ad020b4a925 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -63,6 +63,7 @@ struct i915_params { bool nuclear_pageflip; bool enable_dp_mst; bool enable_dpcd_backlight; + bool enable_gvt; }; extern struct i915_params i915 __read_mostly; diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h new file mode 100644 index 000000000000..c0cb2974caac --- /dev/null +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -0,0 +1,113 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _I915_PVINFO_H_ +#define _I915_PVINFO_H_ + +/* The MMIO offset of the shared info between guest and host emulator */ +#define VGT_PVINFO_PAGE 0x78000 +#define VGT_PVINFO_SIZE 0x1000 + +/* + * The following structure pages are defined in GEN MMIO space + * for virtualization. (One page for now) + */ +#define VGT_MAGIC 0x4776544776544776ULL /* 'vGTvGTvG' */ +#define VGT_VERSION_MAJOR 1 +#define VGT_VERSION_MINOR 0 + +#define INTEL_VGT_IF_VERSION_ENCODE(major, minor) ((major) << 16 | (minor)) +#define INTEL_VGT_IF_VERSION \ + INTEL_VGT_IF_VERSION_ENCODE(VGT_VERSION_MAJOR, VGT_VERSION_MINOR) + +/* + * notifications from guest to vgpu device model + */ +enum vgt_g2v_type { + VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE = 2, + VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY, + VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE, + VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY, + VGT_G2V_EXECLIST_CONTEXT_CREATE, + VGT_G2V_EXECLIST_CONTEXT_DESTROY, + VGT_G2V_MAX, +}; + +struct vgt_if { + u64 magic; /* VGT_MAGIC */ + uint16_t version_major; + uint16_t version_minor; + u32 vgt_id; /* ID of vGT instance */ + u32 rsv1[12]; /* pad to offset 0x40 */ + /* + * Data structure to describe the balooning info of resources. + * Each VM can only have one portion of continuous area for now. + * (May support scattered resource in future) + * (starting from offset 0x40) + */ + struct { + /* Aperture register balooning */ + struct { + u32 base; + u32 size; + } mappable_gmadr; /* aperture */ + /* GMADR register balooning */ + struct { + u32 base; + u32 size; + } nonmappable_gmadr; /* non aperture */ + /* allowed fence registers */ + u32 fence_num; + u32 rsv2[3]; + } avail_rs; /* available/assigned resource */ + u32 rsv3[0x200 - 24]; /* pad to half page */ + /* + * The bottom half page is for response from Gfx driver to hypervisor. + */ + u32 rsv4; + u32 display_ready; /* ready for display owner switch */ + + u32 rsv5[4]; + + u32 g2v_notify; + u32 rsv6[7]; + + struct { + u32 lo; + u32 hi; + } pdp[4]; + + u32 execlist_context_descriptor_lo; + u32 execlist_context_descriptor_hi; + + u32 rsv7[0x200 - 24]; /* pad to one page */ +} __packed; + +#define vgtif_reg(x) \ + _MMIO((VGT_PVINFO_PAGE + offsetof(struct vgt_if, x))) + +/* vGPU display status to be used by the host side */ +#define VGT_DRV_DISPLAY_NOT_READY 0 +#define VGT_DRV_DISPLAY_READY 1 /* ready for display switch */ + +#endif /* _I915_PVINFO_H_ */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index dfb4c7a88de3..c6bfbf8d7cca 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -220,6 +220,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define ECOCHK_PPGTT_WT_HSW (0x2<<3) #define ECOCHK_PPGTT_WB_HSW (0x3<<3) +#define GEN8_CONFIG0 _MMIO(0xD00) +#define GEN9_DEFAULT_FIXES (1 << 3 | 1 << 2 | 1 << 1) + #define GAC_ECO_BITS _MMIO(0x14090) #define ECOBITS_SNB_BIT (1<<13) #define ECOBITS_PPGTT_CACHE64B (3<<8) @@ -442,6 +445,8 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) */ #define GFX_INSTR(opcode, flags) ((0x3 << 29) | ((opcode) << 24) | (flags)) +#define GEN9_MEDIA_POOL_STATE ((0x3 << 29) | (0x2 << 27) | (0x5 << 16) | 4) +#define GEN9_MEDIA_POOL_ENABLE (1 << 31) #define GFX_OP_RASTER_RULES ((0x3<<29)|(0x7<<24)) #define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) #define SC_UPDATE_SCISSOR (0x1<<1) @@ -713,6 +718,9 @@ enum skl_disp_power_wells { /* Not actual bit groups. Used as IDs for lookup_power_well() */ SKL_DISP_PW_ALWAYS_ON, SKL_DISP_PW_DC_OFF, + + BXT_DPIO_CMN_A, + BXT_DPIO_CMN_BC, }; #define SKL_POWER_WELL_STATE(pw) (1 << ((pw) * 2)) @@ -1273,6 +1281,15 @@ enum skl_disp_power_wells { #define BXT_P_CR_GT_DISP_PWRON _MMIO(0x138090) #define GT_DISPLAY_POWER_ON(phy) (1 << (phy)) +#define _BXT_PHY_CTL_DDI_A 0x64C00 +#define _BXT_PHY_CTL_DDI_B 0x64C10 +#define _BXT_PHY_CTL_DDI_C 0x64C20 +#define BXT_PHY_CMNLANE_POWERDOWN_ACK (1 << 10) +#define BXT_PHY_LANE_POWERDOWN_ACK (1 << 9) +#define BXT_PHY_LANE_ENABLED (1 << 8) +#define BXT_PHY_CTL(port) _MMIO_PORT(port, _BXT_PHY_CTL_DDI_A, \ + _BXT_PHY_CTL_DDI_B) + #define _PHY_CTL_FAMILY_EDP 0x64C80 #define _PHY_CTL_FAMILY_DDI 0x64C90 #define COMMON_RESET_DIS (1 << 31) @@ -1669,6 +1686,9 @@ enum skl_disp_power_wells { #define GEN7_TLB_RD_ADDR _MMIO(0x4700) +#define GAMT_CHKN_BIT_REG _MMIO(0x4ab8) +#define GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING (1<<28) + #if 0 #define PRB0_TAIL _MMIO(0x2030) #define PRB0_HEAD _MMIO(0x2034) @@ -1804,6 +1824,10 @@ enum skl_disp_power_wells { #define GEN9_IZ_HASHING_MASK(slice) (0x3 << ((slice) * 2)) #define GEN9_IZ_HASHING(slice, val) ((val) << ((slice) * 2)) +/* chicken reg for WaConextSwitchWithConcurrentTLBInvalidate */ +#define GEN9_CSFE_CHICKEN1_RCS _MMIO(0x20D4) +#define GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE (1 << 2) + /* WaClearTdlStateAckDirtyBits */ #define GEN8_STATE_ACK _MMIO(0x20F0) #define GEN9_STATE_ACK_SLICE1 _MMIO(0x20F8) @@ -2161,6 +2185,9 @@ enum skl_disp_power_wells { #define FBC_LL_SIZE (1536) +#define FBC_LLC_READ_CTRL _MMIO(0x9044) +#define FBC_LLC_FULLY_OPEN (1<<30) + /* Framebuffer compression for GM45+ */ #define DPFC_CB_BASE _MMIO(0x3200) #define DPFC_CONTROL _MMIO(0x3208) @@ -2200,6 +2227,8 @@ enum skl_disp_power_wells { #define ILK_DPFC_STATUS _MMIO(0x43210) #define ILK_DPFC_FENCE_YOFF _MMIO(0x43218) #define ILK_DPFC_CHICKEN _MMIO(0x43224) +#define ILK_DPFC_DISABLE_DUMMY0 (1<<8) +#define ILK_DPFC_NUKE_ON_ANY_MODIFICATION (1<<23) #define ILK_FBC_RT_BASE _MMIO(0x2128) #define ILK_FBC_RT_VALID (1<<0) #define SNB_FBC_FRONT_BUFFER (1<<1) @@ -3022,6 +3051,18 @@ enum skl_disp_power_wells { /* Same as Haswell, but 72064 bytes now. */ #define GEN8_CXT_TOTAL_SIZE (18 * PAGE_SIZE) +enum { + INTEL_ADVANCED_CONTEXT = 0, + INTEL_LEGACY_32B_CONTEXT, + INTEL_ADVANCED_AD_CONTEXT, + INTEL_LEGACY_64B_CONTEXT +}; + +#define GEN8_CTX_ADDRESSING_MODE_SHIFT 3 +#define GEN8_CTX_ADDRESSING_MODE(dev_priv) (USES_FULL_48BIT_PPGTT(dev_priv) ?\ + INTEL_LEGACY_64B_CONTEXT : \ + INTEL_LEGACY_32B_CONTEXT) + #define CHV_CLK_CTL1 _MMIO(0x101100) #define VLV_CLK_CTL2 _MMIO(0x101104) #define CLK_CTL2_CZCOUNT_30NS_SHIFT 28 @@ -6035,6 +6076,9 @@ enum skl_disp_power_wells { #define FORCE_ARB_IDLE_PLANES (1 << 14) #define SKL_EDP_PSR_FIX_RDWRAP (1 << 3) +#define CHICKEN_PAR2_1 _MMIO(0x42090) +#define KVM_CONFIG_CHANGE_NOTIFICATION_SELECT (1 << 14) + #define _CHICKEN_PIPESL_1_A 0x420b0 #define _CHICKEN_PIPESL_1_B 0x420b4 #define HSW_FBCQ_DIS (1 << 22) @@ -6042,6 +6086,7 @@ enum skl_disp_power_wells { #define CHICKEN_PIPESL_1(pipe) _MMIO_PIPE(pipe, _CHICKEN_PIPESL_1_A, _CHICKEN_PIPESL_1_B) #define DISP_ARB_CTL _MMIO(0x45000) +#define DISP_FBC_MEMORY_WAKE (1<<31) #define DISP_TILE_SURFACE_SWIZZLING (1<<13) #define DISP_FBC_WM_DIS (1<<15) #define DISP_ARB_CTL2 _MMIO(0x45004) @@ -6055,6 +6100,9 @@ enum skl_disp_power_wells { #define HSW_NDE_RSTWRN_OPT _MMIO(0x46408) #define RESET_PCH_HANDSHAKE_ENABLE (1<<4) +#define GEN8_CHICKEN_DCPR_1 _MMIO(0x46430) +#define MASK_WAKEMEM (1<<13) + #define SKL_DFSM _MMIO(0x51000) #define SKL_DFSM_CDCLK_LIMIT_MASK (3 << 23) #define SKL_DFSM_CDCLK_LIMIT_675 (0 << 23) @@ -6070,8 +6118,10 @@ enum skl_disp_power_wells { #define FF_SLICE_CS_CHICKEN2 _MMIO(0x20e4) #define GEN9_TSG_BARRIER_ACK_DISABLE (1<<8) +#define GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE (1<<10) #define GEN9_CS_DEBUG_MODE1 _MMIO(0x20ec) +#define GEN9_CTX_PREEMPT_REG _MMIO(0x2248) #define GEN8_CS_CHICKEN1 _MMIO(0x2580) /* GEN7 chicken */ @@ -6079,6 +6129,7 @@ enum skl_disp_power_wells { # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26)) # define GEN9_RHWO_OPTIMIZATION_DISABLE (1<<14) #define COMMON_SLICE_CHICKEN2 _MMIO(0x7014) +# define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8) # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE (1<<0) #define HIZ_CHICKEN _MMIO(0x7018) @@ -6931,6 +6982,7 @@ enum skl_disp_power_wells { #define EDRAM_SETS_IDX(cap) (((cap) >> 8) & 0x3) #define GEN6_UCGCTL1 _MMIO(0x9400) +# define GEN6_GAMUNIT_CLOCK_GATE_DISABLE (1 << 22) # define GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE (1 << 16) # define GEN6_BLBUNIT_CLOCK_GATE_DISABLE (1 << 5) # define GEN6_CSUNIT_CLOCK_GATE_DISABLE (1 << 7) @@ -6947,6 +6999,7 @@ enum skl_disp_power_wells { #define GEN7_UCGCTL4 _MMIO(0x940c) #define GEN7_L3BANK2X_CLOCK_GATE_DISABLE (1<<25) +#define GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE (1<<14) #define GEN6_RCGCTL1 _MMIO(0x9410) #define GEN6_RCGCTL2 _MMIO(0x9414) @@ -8151,6 +8204,8 @@ enum skl_disp_power_wells { #define _MIPIA_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb05c) #define _MIPIC_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb85c) #define MIPI_EOT_DISABLE(port) _MMIO_MIPI(port, _MIPIA_EOT_DISABLE, _MIPIC_EOT_DISABLE) +#define BXT_DEFEATURE_DPI_FIFO_CTR (1 << 9) +#define BXT_DPHY_DEFEATURE_EN (1 << 8) #define LP_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 7) #define HS_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 6) #define LOW_CONTENTION_RECOVERY_DISABLE (1 << 5) diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index 004326291854..f6acb5a0e701 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -53,7 +53,7 @@ /** * i915_check_vgpu - detect virtual GPU - * @dev: drm device * + * @dev_priv: i915 device private * * This function is called at the initialization stage, to detect whether * running on a vGPU. @@ -101,10 +101,13 @@ static struct _balloon_info_ bl_info; * This function is called to deallocate the ballooned-out graphic memory, when * driver is unloaded or when ballooning fails. */ -void intel_vgt_deballoon(void) +void intel_vgt_deballoon(struct drm_i915_private *dev_priv) { int i; + if (!intel_vgpu_active(dev_priv)) + return; + DRM_DEBUG("VGT deballoon.\n"); for (i = 0; i < 4; i++) { @@ -135,7 +138,7 @@ static int vgt_balloon_space(struct drm_mm *mm, /** * intel_vgt_balloon - balloon out reserved graphics address trunks - * @dev_priv: i915 device + * @dev: drm device * * This function is called at the initialization stage, to balloon out the * graphic address space allocated to other vGPUs, by marking these spaces as @@ -177,9 +180,8 @@ static int vgt_balloon_space(struct drm_mm *mm, * Returns: * zero on success, non-zero if configuration invalid or ballooning failed */ -int intel_vgt_balloon(struct drm_device *dev) +int intel_vgt_balloon(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; unsigned long ggtt_end = ggtt->base.start + ggtt->base.total; @@ -187,6 +189,9 @@ int intel_vgt_balloon(struct drm_device *dev) unsigned long unmappable_base, unmappable_size, unmappable_end; int ret; + if (!intel_vgpu_active(dev_priv)) + return 0; + mappable_base = I915_READ(vgtif_reg(avail_rs.mappable_gmadr.base)); mappable_size = I915_READ(vgtif_reg(avail_rs.mappable_gmadr.size)); unmappable_base = I915_READ(vgtif_reg(avail_rs.nonmappable_gmadr.base)); @@ -258,6 +263,6 @@ int intel_vgt_balloon(struct drm_device *dev) err: DRM_ERROR("VGT balloon fail\n"); - intel_vgt_deballoon(); + intel_vgt_deballoon(dev_priv); return ret; } diff --git a/drivers/gpu/drm/i915/i915_vgpu.h b/drivers/gpu/drm/i915/i915_vgpu.h index 21ffcfea5f5d..3c3b2d24e830 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.h +++ b/drivers/gpu/drm/i915/i915_vgpu.h @@ -24,94 +24,10 @@ #ifndef _I915_VGPU_H_ #define _I915_VGPU_H_ -/* The MMIO offset of the shared info between guest and host emulator */ -#define VGT_PVINFO_PAGE 0x78000 -#define VGT_PVINFO_SIZE 0x1000 +#include "i915_pvinfo.h" -/* - * The following structure pages are defined in GEN MMIO space - * for virtualization. (One page for now) - */ -#define VGT_MAGIC 0x4776544776544776ULL /* 'vGTvGTvG' */ -#define VGT_VERSION_MAJOR 1 -#define VGT_VERSION_MINOR 0 - -#define INTEL_VGT_IF_VERSION_ENCODE(major, minor) ((major) << 16 | (minor)) -#define INTEL_VGT_IF_VERSION \ - INTEL_VGT_IF_VERSION_ENCODE(VGT_VERSION_MAJOR, VGT_VERSION_MINOR) - -/* - * notifications from guest to vgpu device model - */ -enum vgt_g2v_type { - VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE = 2, - VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY, - VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE, - VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY, - VGT_G2V_EXECLIST_CONTEXT_CREATE, - VGT_G2V_EXECLIST_CONTEXT_DESTROY, - VGT_G2V_MAX, -}; - -struct vgt_if { - uint64_t magic; /* VGT_MAGIC */ - uint16_t version_major; - uint16_t version_minor; - uint32_t vgt_id; /* ID of vGT instance */ - uint32_t rsv1[12]; /* pad to offset 0x40 */ - /* - * Data structure to describe the balooning info of resources. - * Each VM can only have one portion of continuous area for now. - * (May support scattered resource in future) - * (starting from offset 0x40) - */ - struct { - /* Aperture register balooning */ - struct { - uint32_t base; - uint32_t size; - } mappable_gmadr; /* aperture */ - /* GMADR register balooning */ - struct { - uint32_t base; - uint32_t size; - } nonmappable_gmadr; /* non aperture */ - /* allowed fence registers */ - uint32_t fence_num; - uint32_t rsv2[3]; - } avail_rs; /* available/assigned resource */ - uint32_t rsv3[0x200 - 24]; /* pad to half page */ - /* - * The bottom half page is for response from Gfx driver to hypervisor. - */ - uint32_t rsv4; - uint32_t display_ready; /* ready for display owner switch */ - - uint32_t rsv5[4]; - - uint32_t g2v_notify; - uint32_t rsv6[7]; - - struct { - uint32_t lo; - uint32_t hi; - } pdp[4]; - - uint32_t execlist_context_descriptor_lo; - uint32_t execlist_context_descriptor_hi; - - uint32_t rsv7[0x200 - 24]; /* pad to one page */ -} __packed; - -#define vgtif_reg(x) \ - _MMIO((VGT_PVINFO_PAGE + (long)&((struct vgt_if *)NULL)->x)) - -/* vGPU display status to be used by the host side */ -#define VGT_DRV_DISPLAY_NOT_READY 0 -#define VGT_DRV_DISPLAY_READY 1 /* ready for display switch */ - -extern void i915_check_vgpu(struct drm_i915_private *dev_priv); -extern int intel_vgt_balloon(struct drm_device *dev); -extern void intel_vgt_deballoon(void); +void i915_check_vgpu(struct drm_i915_private *dev_priv); +int intel_vgt_balloon(struct drm_i915_private *dev_priv); +void intel_vgt_deballoon(struct drm_i915_private *dev_priv); #endif /* _I915_VGPU_H_ */ diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 713a02db378a..da5ed4a850b9 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1570,6 +1570,45 @@ bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin) } /** + * intel_bios_is_port_present - is the specified digital port present + * @dev_priv: i915 device instance + * @port: port to check + * + * Return true if the device in %port is present. + */ +bool intel_bios_is_port_present(struct drm_i915_private *dev_priv, enum port port) +{ + static const struct { + u16 dp, hdmi; + } port_mapping[] = { + [PORT_B] = { DVO_PORT_DPB, DVO_PORT_HDMIB, }, + [PORT_C] = { DVO_PORT_DPC, DVO_PORT_HDMIC, }, + [PORT_D] = { DVO_PORT_DPD, DVO_PORT_HDMID, }, + [PORT_E] = { DVO_PORT_DPE, DVO_PORT_HDMIE, }, + }; + int i; + + /* FIXME maybe deal with port A as well? */ + if (WARN_ON(port == PORT_A) || port >= ARRAY_SIZE(port_mapping)) + return false; + + if (!dev_priv->vbt.child_dev_num) + return false; + + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + const union child_device_config *p_child = + &dev_priv->vbt.child_dev[i]; + if ((p_child->common.dvo_port == port_mapping[port].dp || + p_child->common.dvo_port == port_mapping[port].hdmi) && + (p_child->common.device_type & (DEVICE_TYPE_TMDS_DVI_SIGNALING | + DEVICE_TYPE_DISPLAYPORT_OUTPUT))) + return true; + } + + return false; +} + +/** * intel_bios_is_port_edp - is the device in given port eDP * @dev_priv: i915 device instance * @port: port to check diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 9465de4135aa..e115bcc6766f 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -743,6 +743,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = intel_crt_detect, .fill_modes = drm_helper_probe_single_connector_modes, + .early_unregister = intel_connector_unregister, .destroy = intel_crt_destroy, .set_property = intel_crt_set_property, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -875,7 +876,6 @@ void intel_crt_init(struct drm_device *dev) crt->base.get_hw_state = intel_crt_get_hw_state; } intel_connector->get_hw_state = intel_connector_get_hw_state; - intel_connector->unregister = intel_connector_unregister; drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 022b41d422dc..ad3b0ee5e55b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1342,6 +1342,14 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port)); out: + if (ret && IS_BROXTON(dev_priv)) { + tmp = I915_READ(BXT_PHY_CTL(port)); + if ((tmp & (BXT_PHY_LANE_POWERDOWN_ACK | + BXT_PHY_LANE_ENABLED)) != BXT_PHY_LANE_ENABLED) + DRM_ERROR("Port %c enabled but PHY powered down? " + "(PHY_CTL %08x)\n", port_name(port), tmp); + } + intel_display_power_put(dev_priv, power_domain); return ret; @@ -1742,9 +1750,11 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) } } -static bool broxton_phy_is_enabled(struct drm_i915_private *dev_priv, - enum dpio_phy phy) +bool bxt_ddi_phy_is_enabled(struct drm_i915_private *dev_priv, + enum dpio_phy phy) { + enum port port; + if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & GT_DISPLAY_POWER_ON(phy))) return false; @@ -1770,38 +1780,48 @@ static bool broxton_phy_is_enabled(struct drm_i915_private *dev_priv, return false; } + for_each_port_masked(port, + phy == DPIO_PHY0 ? BIT(PORT_B) | BIT(PORT_C) : + BIT(PORT_A)) { + u32 tmp = I915_READ(BXT_PHY_CTL(port)); + + if (tmp & BXT_PHY_CMNLANE_POWERDOWN_ACK) { + DRM_DEBUG_DRIVER("DDI PHY %d powered, but common lane " + "for port %c powered down " + "(PHY_CTL %08x)\n", + phy, port_name(port), tmp); + + return false; + } + } + return true; } -static u32 broxton_get_grc(struct drm_i915_private *dev_priv, enum dpio_phy phy) +static u32 bxt_get_grc(struct drm_i915_private *dev_priv, enum dpio_phy phy) { u32 val = I915_READ(BXT_PORT_REF_DW6(phy)); return (val & GRC_CODE_MASK) >> GRC_CODE_SHIFT; } -static void broxton_phy_wait_grc_done(struct drm_i915_private *dev_priv, - enum dpio_phy phy) +static void bxt_phy_wait_grc_done(struct drm_i915_private *dev_priv, + enum dpio_phy phy) { if (wait_for(I915_READ(BXT_PORT_REF_DW3(phy)) & GRC_DONE, 10)) DRM_ERROR("timeout waiting for PHY%d GRC\n", phy); } -static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv, - enum dpio_phy phy); - -static void broxton_phy_init(struct drm_i915_private *dev_priv, - enum dpio_phy phy) +void bxt_ddi_phy_init(struct drm_i915_private *dev_priv, enum dpio_phy phy) { - enum port port; - u32 ports, val; + u32 val; - if (broxton_phy_is_enabled(dev_priv, phy)) { + if (bxt_ddi_phy_is_enabled(dev_priv, phy)) { /* Still read out the GRC value for state verification */ if (phy == DPIO_PHY0) - dev_priv->bxt_phy_grc = broxton_get_grc(dev_priv, phy); + dev_priv->bxt_phy_grc = bxt_get_grc(dev_priv, phy); - if (broxton_phy_verify_state(dev_priv, phy)) { + if (bxt_ddi_phy_verify_state(dev_priv, phy)) { DRM_DEBUG_DRIVER("DDI PHY %d already enabled, " "won't reprogram it\n", phy); @@ -1810,8 +1830,6 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv, DRM_DEBUG_DRIVER("DDI PHY %d enabled with invalid state, " "force reprogramming it\n", phy); - } else { - DRM_DEBUG_DRIVER("DDI PHY %d not enabled, enabling it\n", phy); } val = I915_READ(BXT_P_CR_GT_DISP_PWRON); @@ -1831,28 +1849,6 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv, DRM_ERROR("timeout during PHY%d power on\n", phy); } - if (phy == DPIO_PHY0) - ports = BIT(PORT_B) | BIT(PORT_C); - else - ports = BIT(PORT_A); - - for_each_port_masked(port, ports) { - int lane; - - for (lane = 0; lane < 4; lane++) { - val = I915_READ(BXT_PORT_TX_DW14_LN(port, lane)); - /* - * Note that on CHV this flag is called UPAR, but has - * the same function. - */ - val &= ~LATENCY_OPTIM; - if (lane != 1) - val |= LATENCY_OPTIM; - - I915_WRITE(BXT_PORT_TX_DW14_LN(port, lane), val); - } - } - /* Program PLL Rcomp code offset */ val = I915_READ(BXT_PORT_CL1CM_DW9(phy)); val &= ~IREF0RC_OFFSET_MASK; @@ -1899,10 +1895,7 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv, * the corresponding calibrated value from PHY1, and disable * the automatic calibration on PHY0. */ - broxton_phy_wait_grc_done(dev_priv, DPIO_PHY1); - - val = dev_priv->bxt_phy_grc = broxton_get_grc(dev_priv, - DPIO_PHY1); + val = dev_priv->bxt_phy_grc = bxt_get_grc(dev_priv, DPIO_PHY1); grc_code = val << GRC_CODE_FAST_SHIFT | val << GRC_CODE_SLOW_SHIFT | val; @@ -1912,31 +1905,16 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv, val |= GRC_DIS | GRC_RDY_OVRD; I915_WRITE(BXT_PORT_REF_DW8(DPIO_PHY0), val); } - /* - * During PHY1 init delay waiting for GRC calibration to finish, since - * it can happen in parallel with the subsequent PHY0 init. - */ val = I915_READ(BXT_PHY_CTL_FAMILY(phy)); val |= COMMON_RESET_DIS; I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val); -} -void broxton_ddi_phy_init(struct drm_i915_private *dev_priv) -{ - /* Enable PHY1 first since it provides Rcomp for PHY0 */ - broxton_phy_init(dev_priv, DPIO_PHY1); - broxton_phy_init(dev_priv, DPIO_PHY0); - - /* - * If BIOS enabled only PHY0 and not PHY1, we skipped waiting for the - * PHY1 GRC calibration to finish, so wait for it here. - */ - broxton_phy_wait_grc_done(dev_priv, DPIO_PHY1); + if (phy == DPIO_PHY1) + bxt_phy_wait_grc_done(dev_priv, DPIO_PHY1); } -static void broxton_phy_uninit(struct drm_i915_private *dev_priv, - enum dpio_phy phy) +void bxt_ddi_phy_uninit(struct drm_i915_private *dev_priv, enum dpio_phy phy) { uint32_t val; @@ -1949,12 +1927,6 @@ static void broxton_phy_uninit(struct drm_i915_private *dev_priv, I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val); } -void broxton_ddi_phy_uninit(struct drm_i915_private *dev_priv) -{ - broxton_phy_uninit(dev_priv, DPIO_PHY1); - broxton_phy_uninit(dev_priv, DPIO_PHY0); -} - static bool __printf(6, 7) __phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy, i915_reg_t reg, u32 mask, u32 expected, @@ -1982,11 +1954,9 @@ __phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy, return false; } -static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv, - enum dpio_phy phy) +bool bxt_ddi_phy_verify_state(struct drm_i915_private *dev_priv, + enum dpio_phy phy) { - enum port port; - u32 ports; uint32_t mask; bool ok; @@ -1994,27 +1964,11 @@ static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv, __phy_reg_verify_state(dev_priv, phy, reg, mask, exp, fmt, \ ## __VA_ARGS__) - /* We expect the PHY to be always enabled */ - if (!broxton_phy_is_enabled(dev_priv, phy)) + if (!bxt_ddi_phy_is_enabled(dev_priv, phy)) return false; ok = true; - if (phy == DPIO_PHY0) - ports = BIT(PORT_B) | BIT(PORT_C); - else - ports = BIT(PORT_A); - - for_each_port_masked(port, ports) { - int lane; - - for (lane = 0; lane < 4; lane++) - ok &= _CHK(BXT_PORT_TX_DW14_LN(port, lane), - LATENCY_OPTIM, - lane != 1 ? LATENCY_OPTIM : 0, - "BXT_PORT_TX_DW14_LN(%d, %d)", port, lane); - } - /* PLL Rcomp code offset */ ok &= _CHK(BXT_PORT_CL1CM_DW9(phy), IREF0RC_OFFSET_MASK, 0xe4 << IREF0RC_OFFSET_SHIFT, @@ -2058,11 +2012,65 @@ static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv, #undef _CHK } -void broxton_ddi_phy_verify_state(struct drm_i915_private *dev_priv) +static uint8_t +bxt_ddi_phy_calc_lane_lat_optim_mask(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config) { - if (!broxton_phy_verify_state(dev_priv, DPIO_PHY0) || - !broxton_phy_verify_state(dev_priv, DPIO_PHY1)) - i915_report_error(dev_priv, "DDI PHY state mismatch\n"); + switch (pipe_config->lane_count) { + case 1: + return 0; + case 2: + return BIT(2) | BIT(0); + case 4: + return BIT(3) | BIT(2) | BIT(0); + default: + MISSING_CASE(pipe_config->lane_count); + + return 0; + } +} + +static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_i915_private *dev_priv = to_i915(dport->base.base.dev); + enum port port = dport->port; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + int lane; + + for (lane = 0; lane < 4; lane++) { + u32 val = I915_READ(BXT_PORT_TX_DW14_LN(port, lane)); + + /* + * Note that on CHV this flag is called UPAR, but has + * the same function. + */ + val &= ~LATENCY_OPTIM; + if (intel_crtc->config->lane_lat_optim_mask & BIT(lane)) + val |= LATENCY_OPTIM; + + I915_WRITE(BXT_PORT_TX_DW14_LN(port, lane), val); + } +} + +static uint8_t +bxt_ddi_phy_get_lane_lat_optim_mask(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_i915_private *dev_priv = to_i915(dport->base.base.dev); + enum port port = dport->port; + int lane; + uint8_t mask; + + mask = 0; + for (lane = 0; lane < 4; lane++) { + u32 val = I915_READ(BXT_PORT_TX_DW14_LN(port, lane)); + + if (val & LATENCY_OPTIM) + mask |= BIT(lane); + } + + return mask; } void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp) @@ -2236,13 +2244,19 @@ void intel_ddi_get_config(struct intel_encoder *encoder, } intel_ddi_clock_get(encoder, pipe_config); + + if (IS_BROXTON(dev_priv)) + pipe_config->lane_lat_optim_mask = + bxt_ddi_phy_get_lane_lat_optim_mask(encoder); } static bool intel_ddi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; int type = encoder->type; int port = intel_ddi_get_encoder_port(encoder); + int ret; WARN(type == INTEL_OUTPUT_UNKNOWN, "compute_config() on unknown output!\n"); @@ -2250,9 +2264,17 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder, pipe_config->cpu_transcoder = TRANSCODER_EDP; if (type == INTEL_OUTPUT_HDMI) - return intel_hdmi_compute_config(encoder, pipe_config); + ret = intel_hdmi_compute_config(encoder, pipe_config); else - return intel_dp_compute_config(encoder, pipe_config); + ret = intel_dp_compute_config(encoder, pipe_config); + + if (IS_BROXTON(dev_priv) && ret) + pipe_config->lane_lat_optim_mask = + bxt_ddi_phy_calc_lane_lat_optim_mask(encoder, + pipe_config); + + return ret; + } static const struct drm_encoder_funcs intel_ddi_funcs = { @@ -2351,6 +2373,8 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->compute_config = intel_ddi_compute_config; intel_encoder->enable = intel_enable_ddi; + if (IS_BROXTON(dev_priv)) + intel_encoder->pre_pll_enable = bxt_ddi_pre_pll_enable; intel_encoder->pre_enable = intel_ddi_pre_enable; intel_encoder->disable = intel_disable_ddi; intel_encoder->post_disable = intel_ddi_post_disable; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 49322f6cfa2b..0b2cd669ac05 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -36,6 +36,7 @@ #include "intel_drv.h" #include <drm/i915_drm.h> #include "i915_drv.h" +#include "i915_gem_dmabuf.h" #include "intel_dsi.h" #include "i915_trace.h" #include <drm/drm_atomic.h> @@ -46,7 +47,6 @@ #include <drm/drm_rect.h> #include <linux/dma_remapping.h> #include <linux/reservation.h> -#include <linux/dma-buf.h> static bool is_mmio_work(struct intel_flip_work *work) { @@ -123,7 +123,7 @@ static void ironlake_pfit_enable(struct intel_crtc *crtc); static void intel_modeset_setup_hw_state(struct drm_device *dev); static void intel_pre_disable_primary_noatomic(struct drm_crtc *crtc); static int ilk_max_pixel_rate(struct drm_atomic_state *state); -static int broxton_calc_cdclk(int max_pixclk); +static int bxt_calc_cdclk(int max_pixclk); struct intel_limit { struct { @@ -4641,14 +4641,14 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state) struct intel_plane_state *old_primary_state = to_intel_plane_state(old_pri_state); - intel_fbc_pre_update(crtc); + intel_fbc_pre_update(crtc, pipe_config, primary_state); if (old_primary_state->visible && (modeset || !primary_state->visible)) intel_pre_disable_primary(&crtc->base); } - if (pipe_config->disable_cxsr) { + if (pipe_config->disable_cxsr && HAS_GMCH_DISPLAY(dev)) { crtc->wm.cxsr_allowed = false; /* @@ -4841,6 +4841,10 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A, false); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + if (intel_crtc->config->shared_dpll) intel_enable_shared_dpll(intel_crtc); @@ -5416,7 +5420,7 @@ static void bxt_de_pll_enable(struct drm_i915_private *dev_priv, int vco) dev_priv->cdclk_pll.vco = vco; } -static void broxton_set_cdclk(struct drm_i915_private *dev_priv, int cdclk) +static void bxt_set_cdclk(struct drm_i915_private *dev_priv, int cdclk) { u32 val, divider; int vco, ret; @@ -5541,7 +5545,7 @@ sanitize: dev_priv->cdclk_pll.vco = -1; } -void broxton_init_cdclk(struct drm_i915_private *dev_priv) +void bxt_init_cdclk(struct drm_i915_private *dev_priv) { bxt_sanitize_cdclk(dev_priv); @@ -5553,12 +5557,12 @@ void broxton_init_cdclk(struct drm_i915_private *dev_priv) * - The initial CDCLK needs to be read from VBT. * Need to make this change after VBT has changes for BXT. */ - broxton_set_cdclk(dev_priv, broxton_calc_cdclk(0)); + bxt_set_cdclk(dev_priv, bxt_calc_cdclk(0)); } -void broxton_uninit_cdclk(struct drm_i915_private *dev_priv) +void bxt_uninit_cdclk(struct drm_i915_private *dev_priv) { - broxton_set_cdclk(dev_priv, dev_priv->cdclk_pll.ref); + bxt_set_cdclk(dev_priv, dev_priv->cdclk_pll.ref); } static int skl_calc_cdclk(int max_pixclk, int vco) @@ -5984,7 +5988,7 @@ static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, return 200000; } -static int broxton_calc_cdclk(int max_pixclk) +static int bxt_calc_cdclk(int max_pixclk) { if (max_pixclk > 576000) return 624000; @@ -6044,17 +6048,17 @@ static int valleyview_modeset_calc_cdclk(struct drm_atomic_state *state) return 0; } -static int broxton_modeset_calc_cdclk(struct drm_atomic_state *state) +static int bxt_modeset_calc_cdclk(struct drm_atomic_state *state) { int max_pixclk = ilk_max_pixel_rate(state); struct intel_atomic_state *intel_state = to_intel_atomic_state(state); intel_state->cdclk = intel_state->dev_cdclk = - broxton_calc_cdclk(max_pixclk); + bxt_calc_cdclk(max_pixclk); if (!intel_state->active_crtcs) - intel_state->dev_cdclk = broxton_calc_cdclk(0); + intel_state->dev_cdclk = bxt_calc_cdclk(0); return 0; } @@ -8430,12 +8434,9 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) else final |= DREF_NONSPREAD_SOURCE_ENABLE; + final &= ~DREF_SSC_SOURCE_MASK; final &= ~DREF_CPU_SOURCE_OUTPUT_MASK; - - if (!using_ssc_source) { - final &= ~DREF_SSC_SOURCE_MASK; - final &= ~DREF_SSC1_ENABLE; - } + final &= ~DREF_SSC1_ENABLE; if (has_panel) { final |= DREF_SSC_SOURCE_ENABLE; @@ -8450,9 +8451,9 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) 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; + } else if (using_ssc_source) { + final |= DREF_SSC_SOURCE_ENABLE; + final |= DREF_SSC1_ENABLE; } if (final == val) @@ -9673,14 +9674,14 @@ void hsw_disable_pc8(struct drm_i915_private *dev_priv) } } -static void broxton_modeset_commit_cdclk(struct drm_atomic_state *old_state) +static void bxt_modeset_commit_cdclk(struct drm_atomic_state *old_state) { struct drm_device *dev = old_state->dev; struct intel_atomic_state *old_intel_state = to_intel_atomic_state(old_state); unsigned int req_cdclk = old_intel_state->dev_cdclk; - broxton_set_cdclk(to_i915(dev), req_cdclk); + bxt_set_cdclk(to_i915(dev), req_cdclk); } /* compute the max rate for new configuration */ @@ -11428,6 +11429,8 @@ static int intel_gen7_queue_flip(struct drm_device *dev, static bool use_mmio_flip(struct intel_engine_cs *engine, struct drm_i915_gem_object *obj) { + struct reservation_object *resv; + /* * This is not being used for older platforms, because * non-availability of flip done interrupt forces us to use @@ -11448,12 +11451,12 @@ static bool use_mmio_flip(struct intel_engine_cs *engine, return true; else if (i915.enable_execlists) return true; - else if (obj->base.dma_buf && - !reservation_object_test_signaled_rcu(obj->base.dma_buf->resv, - false)) + + resv = i915_gem_object_get_dmabuf_resv(obj); + if (resv && !reservation_object_test_signaled_rcu(resv, false)) return true; - else - return engine != i915_gem_request_get_engine(obj->last_write_req); + + return engine != i915_gem_request_get_engine(obj->last_write_req); } static void skl_do_mmio_flip(struct intel_crtc *intel_crtc, @@ -11542,6 +11545,7 @@ static void intel_mmio_flip_work_func(struct work_struct *w) struct intel_framebuffer *intel_fb = to_intel_framebuffer(crtc->base.primary->fb); struct drm_i915_gem_object *obj = intel_fb->obj; + struct reservation_object *resv; if (work->flip_queued_req) WARN_ON(__i915_wait_request(work->flip_queued_req, @@ -11549,9 +11553,9 @@ static void intel_mmio_flip_work_func(struct work_struct *w) &dev_priv->rps.mmioflips)); /* For framebuffer backed by dmabuf, wait for fence */ - if (obj->base.dma_buf) - WARN_ON(reservation_object_wait_timeout_rcu(obj->base.dma_buf->resv, - false, false, + resv = i915_gem_object_get_dmabuf_resv(obj); + if (resv) + WARN_ON(reservation_object_wait_timeout_rcu(resv, false, false, MAX_SCHEDULE_TIMEOUT) < 0); intel_pipe_update_start(crtc); @@ -11642,6 +11646,7 @@ void intel_check_page_flip(struct drm_i915_private *dev_priv, int pipe) spin_unlock(&dev->event_lock); } +__maybe_unused static int intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -11727,7 +11732,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, crtc->primary->fb = fb; update_state_fb(crtc->primary); - intel_fbc_pre_update(intel_crtc); + + intel_fbc_pre_update(intel_crtc, intel_crtc->config, + to_intel_plane_state(primary->state)); work->pending_flip_obj = obj; @@ -12816,6 +12823,7 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(has_dp_encoder); PIPE_CONF_CHECK_I(lane_count); + PIPE_CONF_CHECK_X(lane_lat_optim_mask); if (INTEL_INFO(dev)->gen < 8) { PIPE_CONF_CHECK_M_N(dp_m_n); @@ -13567,11 +13575,6 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, struct drm_crtc *crtc; int i, ret; - if (nonblock) { - DRM_DEBUG_KMS("i915 does not yet support nonblocking commit\n"); - return -EINVAL; - } - for_each_crtc_in_state(state, crtc, crtc_state, i) { if (state->legacy_cursor_update) continue; @@ -13690,46 +13693,36 @@ static bool needs_vblank_wait(struct intel_crtc_state *crtc_state) return false; } -/** - * intel_atomic_commit - commit validated state object - * @dev: DRM device - * @state: the top-level driver state object - * @nonblock: nonblocking commit - * - * This function commits a top-level state object that has been validated - * with drm_atomic_helper_check(). - * - * FIXME: Atomic modeset support for i915 is not yet complete. At the moment - * we can only handle plane-related operations and do not yet support - * nonblocking commit. - * - * RETURNS - * Zero for success or -errno. - */ -static int intel_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool nonblock) +static void intel_atomic_commit_tail(struct drm_atomic_state *state) { + struct drm_device *dev = state->dev; struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc_state *old_crtc_state; struct drm_crtc *crtc; struct intel_crtc_state *intel_cstate; - int ret = 0, i; + struct drm_plane *plane; + struct drm_plane_state *plane_state; bool hw_check = intel_state->modeset; unsigned long put_domains[I915_MAX_PIPES] = {}; unsigned crtc_vblank_mask = 0; + int i, ret; - ret = intel_atomic_prepare_commit(dev, state, nonblock); - if (ret) { - DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret); - return ret; + for_each_plane_in_state(state, plane, plane_state, i) { + struct intel_plane_state *intel_plane_state = + to_intel_plane_state(plane_state); + + if (!intel_plane_state->wait_req) + continue; + + ret = __i915_wait_request(intel_plane_state->wait_req, + true, NULL, NULL); + /* EIO should be eaten, and we can't get interrupted in the + * worker, and blocking commits have waited already. */ + WARN_ON(ret); } - drm_atomic_helper_swap_state(state, true); - dev_priv->wm.distrust_bios_wm = false; - dev_priv->wm.skl_results = intel_state->wm_results; - intel_shared_dpll_commit(state); + drm_atomic_helper_wait_for_dependencies(state); if (intel_state->modeset) { memcpy(dev_priv->min_pixclk, intel_state->min_pixclk, @@ -13797,30 +13790,44 @@ static int intel_atomic_commit(struct drm_device *dev, bool modeset = needs_modeset(crtc->state); struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc->state); - bool update_pipe = !modeset && pipe_config->update_pipe; if (modeset && crtc->state->active) { update_scanline_offset(to_intel_crtc(crtc)); dev_priv->display.crtc_enable(crtc); } + /* Complete events for now disable pipes here. */ + if (modeset && !crtc->state->active && crtc->state->event) { + spin_lock_irq(&dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&dev->event_lock); + + crtc->state->event = NULL; + } + if (!modeset) intel_pre_plane_update(to_intel_crtc_state(old_crtc_state)); if (crtc->state->active && drm_atomic_get_existing_plane_state(state, crtc->primary)) - intel_fbc_enable(intel_crtc); + intel_fbc_enable(intel_crtc, pipe_config, to_intel_plane_state(crtc->primary->state)); - if (crtc->state->active && - (crtc->state->planes_changed || update_pipe)) + if (crtc->state->active) drm_atomic_helper_commit_planes_on_crtc(old_crtc_state); if (pipe_config->base.active && needs_vblank_wait(pipe_config)) crtc_vblank_mask |= 1 << i; } - /* FIXME: add subpixel order */ - + /* FIXME: We should call drm_atomic_helper_commit_hw_done() here + * already, but still need the state for the delayed optimization. To + * fix this: + * - wrap the optimization/post_plane_update stuff into a per-crtc work. + * - schedule that vblank worker _before_ calling hw_done + * - at the start of commit_tail, cancel it _synchrously + * - switch over to the vblank wait helper in the core after that since + * we don't need out special handling any more. + */ if (!state->legacy_cursor_update) intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask); @@ -13847,6 +13854,8 @@ static int intel_atomic_commit(struct drm_device *dev, intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state); } + drm_atomic_helper_commit_hw_done(state); + if (intel_state->modeset) intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET); @@ -13854,6 +13863,8 @@ static int intel_atomic_commit(struct drm_device *dev, drm_atomic_helper_cleanup_planes(dev, state); mutex_unlock(&dev->struct_mutex); + drm_atomic_helper_commit_cleanup_done(state); + drm_atomic_state_free(state); /* As one of the primary mmio accessors, KMS has a high likelihood @@ -13868,6 +13879,86 @@ static int intel_atomic_commit(struct drm_device *dev, * can happen also when the device is completely off. */ intel_uncore_arm_unclaimed_mmio_detection(dev_priv); +} + +static void intel_atomic_commit_work(struct work_struct *work) +{ + struct drm_atomic_state *state = container_of(work, + struct drm_atomic_state, + commit_work); + intel_atomic_commit_tail(state); +} + +static void intel_atomic_track_fbs(struct drm_atomic_state *state) +{ + struct drm_plane_state *old_plane_state; + struct drm_plane *plane; + struct drm_i915_gem_object *obj, *old_obj; + struct intel_plane *intel_plane; + int i; + + mutex_lock(&state->dev->struct_mutex); + for_each_plane_in_state(state, plane, old_plane_state, i) { + obj = intel_fb_obj(plane->state->fb); + old_obj = intel_fb_obj(old_plane_state->fb); + intel_plane = to_intel_plane(plane); + + i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); + } + mutex_unlock(&state->dev->struct_mutex); +} + +/** + * intel_atomic_commit - commit validated state object + * @dev: DRM device + * @state: the top-level driver state object + * @nonblock: nonblocking commit + * + * This function commits a top-level state object that has been validated + * with drm_atomic_helper_check(). + * + * FIXME: Atomic modeset support for i915 is not yet complete. At the moment + * nonblocking commits are only safe for pure plane updates. Everything else + * should work though. + * + * RETURNS + * Zero for success or -errno. + */ +static int intel_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock) +{ + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = 0; + + if (intel_state->modeset && nonblock) { + DRM_DEBUG_KMS("nonblocking commit for modeset not yet implemented.\n"); + return -EINVAL; + } + + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + + INIT_WORK(&state->commit_work, intel_atomic_commit_work); + + ret = intel_atomic_prepare_commit(dev, state, nonblock); + if (ret) { + DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret); + return ret; + } + + drm_atomic_helper_swap_state(state, true); + dev_priv->wm.distrust_bios_wm = false; + dev_priv->wm.skl_results = intel_state->wm_results; + intel_shared_dpll_commit(state); + intel_atomic_track_fbs(state); + + if (nonblock) + queue_work(system_unbound_wq, &state->commit_work); + else + intel_atomic_commit_tail(state); return 0; } @@ -13917,7 +14008,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .set_property = drm_atomic_helper_crtc_set_property, .destroy = intel_crtc_destroy, - .page_flip = intel_crtc_page_flip, + .page_flip = drm_atomic_helper_page_flip, .atomic_duplicate_state = intel_crtc_duplicate_state, .atomic_destroy_state = intel_crtc_destroy_state, }; @@ -13942,9 +14033,9 @@ intel_prepare_plane_fb(struct drm_plane *plane, { struct drm_device *dev = plane->dev; struct drm_framebuffer *fb = new_state->fb; - struct intel_plane *intel_plane = to_intel_plane(plane); struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->state->fb); + struct reservation_object *resv; int ret = 0; if (!obj && !old_obj) @@ -13974,12 +14065,15 @@ intel_prepare_plane_fb(struct drm_plane *plane, } } + if (!obj) + return 0; + /* For framebuffer backed by dmabuf, wait for fence */ - if (obj && obj->base.dma_buf) { + resv = i915_gem_object_get_dmabuf_resv(obj); + if (resv) { long lret; - lret = reservation_object_wait_timeout_rcu(obj->base.dma_buf->resv, - false, true, + lret = reservation_object_wait_timeout_rcu(resv, false, true, MAX_SCHEDULE_TIMEOUT); if (lret == -ERESTARTSYS) return lret; @@ -13987,9 +14081,7 @@ intel_prepare_plane_fb(struct drm_plane *plane, WARN(lret < 0, "waiting returns %li\n", lret); } - if (!obj) { - ret = 0; - } else if (plane->type == DRM_PLANE_TYPE_CURSOR && + if (plane->type == DRM_PLANE_TYPE_CURSOR && INTEL_INFO(dev)->cursor_needs_physical) { int align = IS_I830(dev) ? 16 * 1024 : 256; ret = i915_gem_object_attach_phys(obj, align); @@ -14000,15 +14092,11 @@ intel_prepare_plane_fb(struct drm_plane *plane, } if (ret == 0) { - if (obj) { - struct intel_plane_state *plane_state = - to_intel_plane_state(new_state); - - i915_gem_request_assign(&plane_state->wait_req, - obj->last_write_req); - } + struct intel_plane_state *plane_state = + to_intel_plane_state(new_state); - i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); + i915_gem_request_assign(&plane_state->wait_req, + obj->last_write_req); } return ret; @@ -14028,7 +14116,6 @@ intel_cleanup_plane_fb(struct drm_plane *plane, const struct drm_plane_state *old_state) { struct drm_device *dev = plane->dev; - struct intel_plane *intel_plane = to_intel_plane(plane); struct intel_plane_state *old_intel_state; struct drm_i915_gem_object *old_obj = intel_fb_obj(old_state->fb); struct drm_i915_gem_object *obj = intel_fb_obj(plane->state->fb); @@ -14042,11 +14129,6 @@ intel_cleanup_plane_fb(struct drm_plane *plane, !INTEL_INFO(dev)->cursor_needs_physical)) intel_unpin_fb_obj(old_state->fb, old_state->rotation); - /* prepare_fb aborted? */ - if ((old_obj && (old_obj->frontbuffer_bits & intel_plane->frontbuffer_bit)) || - (obj && !(obj->frontbuffer_bits & intel_plane->frontbuffer_bit))) - i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); - i915_gem_request_assign(&old_intel_state->wait_req, NULL); } @@ -14102,6 +14184,7 @@ intel_check_primary_plane(struct drm_plane *plane, return drm_plane_helper_check_update(plane, crtc, fb, &state->src, &state->dst, &state->clip, + state->base.rotation, min_scale, max_scale, can_position, true, &state->visible); @@ -14293,6 +14376,7 @@ intel_check_cursor_plane(struct drm_plane *plane, ret = drm_plane_helper_check_update(plane, crtc, fb, &state->src, &state->dst, &state->clip, + state->base.rotation, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, true, true, &state->visible); @@ -14702,7 +14786,7 @@ static void intel_setup_outputs(struct drm_device *dev) if (I915_READ(PCH_DP_D) & DP_DETECTED) intel_dp_init(dev, PCH_DP_D, PORT_D); } else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { - bool has_edp; + bool has_edp, has_port; /* * The DP_DETECTED bit is the latched state of the DDC @@ -14712,25 +14796,37 @@ static void intel_setup_outputs(struct drm_device *dev) * Thus we can't rely on the DP_DETECTED bit alone to detect * eDP ports. Consult the VBT as well as DP_DETECTED to * detect eDP ports. + * + * Sadly the straps seem to be missing sometimes even for HDMI + * ports (eg. on Voyo V3 - CHT x7-Z8700), so check both strap + * and VBT for the presence of the port. Additionally we can't + * trust the port type the VBT declares as we've seen at least + * HDMI ports that the VBT claim are DP or eDP. */ has_edp = intel_dp_is_edp(dev, PORT_B); - if (I915_READ(VLV_DP_B) & DP_DETECTED || has_edp) + has_port = intel_bios_is_port_present(dev_priv, PORT_B); + if (I915_READ(VLV_DP_B) & DP_DETECTED || has_port) has_edp &= intel_dp_init(dev, VLV_DP_B, PORT_B); - if (I915_READ(VLV_HDMIB) & SDVO_DETECTED && !has_edp) + if ((I915_READ(VLV_HDMIB) & SDVO_DETECTED || has_port) && !has_edp) intel_hdmi_init(dev, VLV_HDMIB, PORT_B); has_edp = intel_dp_is_edp(dev, PORT_C); - if (I915_READ(VLV_DP_C) & DP_DETECTED || has_edp) + has_port = intel_bios_is_port_present(dev_priv, PORT_C); + if (I915_READ(VLV_DP_C) & DP_DETECTED || has_port) has_edp &= intel_dp_init(dev, VLV_DP_C, PORT_C); - if (I915_READ(VLV_HDMIC) & SDVO_DETECTED && !has_edp) + if ((I915_READ(VLV_HDMIC) & SDVO_DETECTED || has_port) && !has_edp) intel_hdmi_init(dev, VLV_HDMIC, PORT_C); if (IS_CHERRYVIEW(dev)) { - /* eDP not supported on port D, so don't check VBT */ - if (I915_READ(CHV_HDMID) & SDVO_DETECTED) - intel_hdmi_init(dev, CHV_HDMID, PORT_D); - if (I915_READ(CHV_DP_D) & DP_DETECTED) + /* + * eDP not supported on port D, + * so no need to worry about it + */ + has_port = intel_bios_is_port_present(dev_priv, PORT_D); + if (I915_READ(CHV_DP_D) & DP_DETECTED || has_port) intel_dp_init(dev, CHV_DP_D, PORT_D); + if (I915_READ(CHV_HDMID) & SDVO_DETECTED || has_port) + intel_hdmi_init(dev, CHV_HDMID, PORT_D); } intel_dsi_init(dev); @@ -15212,9 +15308,9 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) valleyview_modeset_calc_cdclk; } else if (IS_BROXTON(dev_priv)) { dev_priv->display.modeset_commit_cdclk = - broxton_modeset_commit_cdclk; + bxt_modeset_commit_cdclk; dev_priv->display.modeset_calc_cdclk = - broxton_modeset_calc_cdclk; + bxt_modeset_calc_cdclk; } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { dev_priv->display.modeset_commit_cdclk = skl_modeset_commit_cdclk; @@ -16217,23 +16313,20 @@ void intel_modeset_gem_init(struct drm_device *dev) intel_backlight_register(dev); } -void intel_connector_unregister(struct intel_connector *intel_connector) +void intel_connector_unregister(struct drm_connector *connector) { - struct drm_connector *connector = &intel_connector->base; + struct intel_connector *intel_connector = to_intel_connector(connector); + intel_backlight_device_unregister(intel_connector); intel_panel_destroy_backlight(connector); - drm_connector_unregister(connector); } void intel_modeset_cleanup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_connector *connector; intel_disable_gt_powersave(dev_priv); - intel_backlight_unregister(dev); - /* * Interrupts and polling as the first thing to avoid creating havoc. * Too much stuff here (turning of connectors, ...) would @@ -16254,9 +16347,7 @@ void intel_modeset_cleanup(struct drm_device *dev) /* flush any delayed tasks or pending work */ flush_scheduled_work(); - /* destroy the backlight and sysfs files before encoders/connectors */ - for_each_intel_connector(dev, connector) - connector->unregister(connector); + drm_connector_unregister_all(dev); drm_mode_config_cleanup(dev); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index be083519dac9..0b84f8e5df50 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1177,7 +1177,6 @@ static void intel_aux_reg_init(struct intel_dp *intel_dp) static void intel_dp_aux_fini(struct intel_dp *intel_dp) { - drm_dp_aux_unregister(&intel_dp->aux); kfree(intel_dp->aux.name); } @@ -1212,15 +1211,6 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) return 0; } -static void -intel_dp_connector_unregister(struct intel_connector *intel_connector) -{ - struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base); - - intel_dp_aux_fini(intel_dp); - intel_connector_unregister(intel_connector); -} - static int intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates) { @@ -4457,6 +4447,13 @@ done: } static void +intel_dp_connector_unregister(struct drm_connector *connector) +{ + drm_dp_aux_unregister(&intel_attached_dp(connector)->aux); + intel_connector_unregister(connector); +} + +static void intel_dp_connector_destroy(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); @@ -4496,6 +4493,9 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) intel_dp->edp_notifier.notifier_call = NULL; } } + + intel_dp_aux_fini(intel_dp); + drm_encoder_cleanup(encoder); kfree(intel_dig_port); } @@ -4572,6 +4572,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, .atomic_get_property = intel_connector_atomic_get_property, + .early_unregister = intel_dp_connector_unregister, .destroy = intel_dp_connector_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -5487,7 +5488,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; - intel_connector->unregister = intel_dp_connector_unregister; /* Set up the hotplug pin. */ switch (port) { diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index f62ca9a126b3..9646816604be 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -336,6 +336,7 @@ static const struct drm_connector_funcs intel_dp_mst_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_mst_set_property, .atomic_get_property = intel_connector_atomic_get_property, + .early_unregister = intel_connector_unregister, .destroy = intel_dp_mst_connector_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -455,7 +456,6 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs); - intel_connector->unregister = intel_connector_unregister; intel_connector->get_hw_state = intel_dp_mst_get_hw_state; intel_connector->mst_port = intel_dp; intel_connector->port = port; @@ -489,7 +489,7 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct intel_connector *intel_connector = to_intel_connector(connector); struct drm_device *dev = connector->dev; - intel_connector->unregister(intel_connector); + drm_connector_unregister(connector); /* need to nuke the connector */ drm_modeset_lock_all(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 270da8de0acf..7d0e071fe355 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -242,14 +242,6 @@ struct intel_connector { * and active (i.e. dpms ON state). */ bool (*get_hw_state)(struct intel_connector *); - /* - * Removes all interfaces through which the connector is accessible - * - like sysfs, debugfs entries -, so that no new operations can be - * started on the connector. Also makes sure all currently pending - * operations finish before returing. - */ - void (*unregister)(struct intel_connector *); - /* Panel info for eDP and LVDS */ struct intel_panel panel; @@ -579,6 +571,12 @@ struct intel_crtc_state { uint8_t lane_count; + /* + * Used by platforms having DP/HDMI PHY with programmable lane + * latency optimization. + */ + uint8_t lane_lat_optim_mask; + /* Panel fitter controls for gen2-gen4 + VLV */ struct { u32 control; @@ -1260,11 +1258,14 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv); void intel_finish_reset(struct drm_i915_private *dev_priv); void hsw_enable_pc8(struct drm_i915_private *dev_priv); void hsw_disable_pc8(struct drm_i915_private *dev_priv); -void broxton_init_cdclk(struct drm_i915_private *dev_priv); -void broxton_uninit_cdclk(struct drm_i915_private *dev_priv); -void broxton_ddi_phy_init(struct drm_i915_private *dev_priv); -void broxton_ddi_phy_uninit(struct drm_i915_private *dev_priv); -void broxton_ddi_phy_verify_state(struct drm_i915_private *dev_priv); +void bxt_init_cdclk(struct drm_i915_private *dev_priv); +void bxt_uninit_cdclk(struct drm_i915_private *dev_priv); +void bxt_ddi_phy_init(struct drm_i915_private *dev_priv, enum dpio_phy phy); +void bxt_ddi_phy_uninit(struct drm_i915_private *dev_priv, enum dpio_phy phy); +bool bxt_ddi_phy_is_enabled(struct drm_i915_private *dev_priv, + enum dpio_phy phy); +bool bxt_ddi_phy_verify_state(struct drm_i915_private *dev_priv, + enum dpio_phy phy); void gen9_sanitize_dc_state(struct drm_i915_private *dev_priv); void bxt_enable_dc9(struct drm_i915_private *dev_priv); void bxt_disable_dc9(struct drm_i915_private *dev_priv); @@ -1422,11 +1423,15 @@ static inline void intel_fbdev_restore_mode(struct drm_device *dev) void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, struct drm_atomic_state *state); bool intel_fbc_is_active(struct drm_i915_private *dev_priv); -void intel_fbc_pre_update(struct intel_crtc *crtc); +void intel_fbc_pre_update(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state); void intel_fbc_post_update(struct intel_crtc *crtc); void intel_fbc_init(struct drm_i915_private *dev_priv); void intel_fbc_init_pipe_state(struct drm_i915_private *dev_priv); -void intel_fbc_enable(struct intel_crtc *crtc); +void intel_fbc_enable(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state); void intel_fbc_disable(struct intel_crtc *crtc); void intel_fbc_global_disable(struct drm_i915_private *dev_priv); void intel_fbc_invalidate(struct drm_i915_private *dev_priv, @@ -1496,7 +1501,14 @@ extern struct drm_display_mode *intel_find_panel_downclock( struct drm_display_mode *fixed_mode, struct drm_connector *connector); void intel_backlight_register(struct drm_device *dev); -void intel_backlight_unregister(struct drm_device *dev); + +#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE) +void intel_backlight_device_unregister(struct intel_connector *connector); +#else /* CONFIG_BACKLIGHT_CLASS_DEVICE */ +static inline void intel_backlight_device_unregister(struct intel_connector *connector) +{ +} +#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ /* intel_psr.c */ diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index a2ead5eac336..b444d0e35a98 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -1172,6 +1172,12 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) if (intel_dsi->clock_stop) tmp |= CLOCKSTOP; + if (IS_BROXTON(dev_priv)) { + tmp |= BXT_DPHY_DEFEATURE_EN; + if (!is_cmd_mode(intel_dsi)) + tmp |= BXT_DEFEATURE_DPI_FIFO_CTR; + } + for_each_dsi_port(port, intel_dsi->ports) { I915_WRITE(MIPI_DSI_FUNC_PRG(port), val); @@ -1384,6 +1390,7 @@ static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs static const struct drm_connector_funcs intel_dsi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = intel_dsi_detect, + .early_unregister = intel_connector_unregister, .destroy = intel_dsi_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dsi_set_property, @@ -1460,7 +1467,6 @@ void intel_dsi_init(struct drm_device *dev) intel_encoder->get_config = intel_dsi_get_config; intel_connector->get_hw_state = intel_connector_get_hw_state; - intel_connector->unregister = intel_connector_unregister; /* * On BYT/CHV, pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index c86f88ed92fd..60e4ddf2ec6d 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -341,6 +341,7 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_connector_funcs intel_dvo_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = intel_dvo_detect, + .early_unregister = intel_connector_unregister, .destroy = intel_dvo_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_get_property = intel_connector_atomic_get_property, @@ -447,7 +448,6 @@ void intel_dvo_init(struct drm_device *dev) intel_encoder->compute_config = intel_dvo_compute_config; intel_encoder->pre_enable = intel_dvo_pre_enable; intel_connector->get_hw_state = intel_dvo_connector_get_hw_state; - intel_connector->unregister = intel_connector_unregister; /* Now, try to find a controller */ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c index 45ee07b888a0..ecabd59ffbaf 100644 --- a/drivers/gpu/drm/i915/intel_fbc.c +++ b/drivers/gpu/drm/i915/intel_fbc.c @@ -481,10 +481,10 @@ static void intel_fbc_deactivate(struct drm_i915_private *dev_priv) intel_fbc_hw_deactivate(dev_priv); } -static bool multiple_pipes_ok(struct intel_crtc *crtc) +static bool multiple_pipes_ok(struct intel_crtc *crtc, + struct intel_plane_state *plane_state) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_plane *primary = crtc->base.primary; + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct intel_fbc *fbc = &dev_priv->fbc; enum pipe pipe = crtc->pipe; @@ -492,9 +492,7 @@ static bool multiple_pipes_ok(struct intel_crtc *crtc) if (!no_fbc_on_multiple_pipes(dev_priv)) return true; - WARN_ON(!drm_modeset_is_locked(&primary->mutex)); - - if (to_intel_plane_state(primary->state)->visible) + if (plane_state->visible) fbc->visible_pipes_mask |= (1 << pipe); else fbc->visible_pipes_mask &= ~(1 << pipe); @@ -709,21 +707,16 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc) return effective_w <= max_w && effective_h <= max_h; } -static void intel_fbc_update_state_cache(struct intel_crtc *crtc) +static void intel_fbc_update_state_cache(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct intel_fbc *fbc = &dev_priv->fbc; struct intel_fbc_state_cache *cache = &fbc->state_cache; - struct intel_crtc_state *crtc_state = - to_intel_crtc_state(crtc->base.state); - struct intel_plane_state *plane_state = - to_intel_plane_state(crtc->base.primary->state); struct drm_framebuffer *fb = plane_state->base.fb; struct drm_i915_gem_object *obj; - WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex)); - WARN_ON(!drm_modeset_is_locked(&crtc->base.primary->mutex)); - cache->crtc.mode_flags = crtc_state->base.adjusted_mode.flags; if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) cache->crtc.hsw_bdw_pixel_rate = @@ -888,7 +881,9 @@ static bool intel_fbc_reg_params_equal(struct intel_fbc_reg_params *params1, return memcmp(params1, params2, sizeof(*params1)) == 0; } -void intel_fbc_pre_update(struct intel_crtc *crtc) +void intel_fbc_pre_update(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct intel_fbc *fbc = &dev_priv->fbc; @@ -898,7 +893,7 @@ void intel_fbc_pre_update(struct intel_crtc *crtc) mutex_lock(&fbc->lock); - if (!multiple_pipes_ok(crtc)) { + if (!multiple_pipes_ok(crtc, plane_state)) { fbc->no_fbc_reason = "more than one pipe active"; goto deactivate; } @@ -906,7 +901,7 @@ void intel_fbc_pre_update(struct intel_crtc *crtc) if (!fbc->enabled || fbc->crtc != crtc) goto unlock; - intel_fbc_update_state_cache(crtc); + intel_fbc_update_state_cache(crtc, crtc_state, plane_state); deactivate: intel_fbc_deactivate(dev_priv); @@ -1090,7 +1085,9 @@ out: * intel_fbc_enable multiple times for the same pipe without an * intel_fbc_disable in the middle, as long as it is deactivated. */ -void intel_fbc_enable(struct intel_crtc *crtc) +void intel_fbc_enable(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct intel_fbc *fbc = &dev_priv->fbc; @@ -1103,19 +1100,19 @@ void intel_fbc_enable(struct intel_crtc *crtc) if (fbc->enabled) { WARN_ON(fbc->crtc == NULL); if (fbc->crtc == crtc) { - WARN_ON(!crtc->config->enable_fbc); + WARN_ON(!crtc_state->enable_fbc); WARN_ON(fbc->active); } goto out; } - if (!crtc->config->enable_fbc) + if (!crtc_state->enable_fbc) goto out; WARN_ON(fbc->active); WARN_ON(fbc->crtc != NULL); - intel_fbc_update_state_cache(crtc); + intel_fbc_update_state_cache(crtc, crtc_state, plane_state); if (intel_fbc_alloc_cfb(crtc)) { fbc->no_fbc_reason = "not enough stolen memory"; goto out; diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 4c725ad6fb54..4babefc51eb2 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -552,8 +552,6 @@ static void intel_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&ifbdev->helper); if (ifbdev->fb) { - drm_framebuffer_unregister_private(&ifbdev->fb->base); - mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0)); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 41601c71f529..4df80cc9a291 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -156,11 +156,11 @@ extern int intel_guc_suspend(struct drm_device *dev); extern int intel_guc_resume(struct drm_device *dev); /* i915_guc_submission.c */ -int i915_guc_submission_init(struct drm_device *dev); -int i915_guc_submission_enable(struct drm_device *dev); +int i915_guc_submission_init(struct drm_i915_private *dev_priv); +int i915_guc_submission_enable(struct drm_i915_private *dev_priv); int i915_guc_wq_check_space(struct drm_i915_gem_request *rq); int i915_guc_submit(struct drm_i915_gem_request *rq); -void i915_guc_submission_disable(struct drm_device *dev); -void i915_guc_submission_fini(struct drm_device *dev); +void i915_guc_submission_disable(struct drm_i915_private *dev_priv); +void i915_guc_submission_fini(struct drm_i915_private *dev_priv); #endif diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index f2b88c7209cb..8fe96a2d989e 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -425,9 +425,13 @@ int intel_guc_setup(struct drm_device *dev) if (!i915.enable_guc_loading) { err = 0; goto fail; - } else if (fw_path == NULL || *fw_path == '\0') { - if (*fw_path == '\0') - DRM_INFO("No GuC firmware known for this platform\n"); + } else if (fw_path == NULL) { + /* Device is known to have no uCode (e.g. no GuC) */ + err = -ENXIO; + goto fail; + } else if (*fw_path == '\0') { + /* Device has a GuC but we don't know what f/w to load? */ + DRM_INFO("No GuC firmware known for this platform\n"); err = -ENODEV; goto fail; } @@ -449,7 +453,7 @@ int intel_guc_setup(struct drm_device *dev) intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); - err = i915_guc_submission_init(dev); + err = i915_guc_submission_init(dev_priv); if (err) goto fail; @@ -488,10 +492,7 @@ int intel_guc_setup(struct drm_device *dev) intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); if (i915.enable_guc_submission) { - /* The execbuf_client will be recreated. Release it first. */ - i915_guc_submission_disable(dev); - - err = i915_guc_submission_enable(dev); + err = i915_guc_submission_enable(dev_priv); if (err) goto fail; direct_interrupts_to_guc(dev_priv); @@ -504,8 +505,8 @@ fail: guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL; direct_interrupts_to_host(dev_priv); - i915_guc_submission_disable(dev); - i915_guc_submission_fini(dev); + i915_guc_submission_disable(dev_priv); + i915_guc_submission_fini(dev_priv); /* * We've failed to load the firmware :( @@ -524,18 +525,20 @@ fail: ret = 0; } - if (err == 0) + if (err == 0 && !HAS_GUC_UCODE(dev)) + ; /* Don't mention the GuC! */ + else if (err == 0) DRM_INFO("GuC firmware load skipped\n"); - else if (ret == -EIO) - DRM_ERROR("GuC firmware load failed: %d\n", err); - else + else if (ret != -EIO) DRM_INFO("GuC firmware load failed: %d\n", err); + else + DRM_ERROR("GuC firmware load failed: %d\n", err); if (i915.enable_guc_submission) { if (fw_path == NULL) DRM_INFO("GuC submission without firmware not supported\n"); if (ret == 0) - DRM_INFO("Falling back to execlist mode\n"); + DRM_INFO("Falling back from GuC submission to execlist mode\n"); else DRM_ERROR("GuC init failed: %d\n", ret); } @@ -730,8 +733,8 @@ void intel_guc_fini(struct drm_device *dev) mutex_lock(&dev->struct_mutex); direct_interrupts_to_host(dev_priv); - i915_guc_submission_disable(dev); - i915_guc_submission_fini(dev); + i915_guc_submission_disable(dev_priv); + i915_guc_submission_fini(dev_priv); if (guc_fw->guc_fw_obj) drm_gem_object_unreference(&guc_fw->guc_fw_obj->base); diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c new file mode 100644 index 000000000000..9fa458ce40a6 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_gvt.c @@ -0,0 +1,100 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "i915_drv.h" +#include "intel_gvt.h" + +/** + * DOC: Intel GVT-g host support + * + * Intel GVT-g is a graphics virtualization technology which shares the + * GPU among multiple virtual machines on a time-sharing basis. Each + * virtual machine is presented a virtual GPU (vGPU), which has equivalent + * features as the underlying physical GPU (pGPU), so i915 driver can run + * seamlessly in a virtual machine. This file provides the englightments + * of GVT and the necessary components used by GVT in i915 driver. + */ + +static bool is_supported_device(struct drm_i915_private *dev_priv) +{ + if (IS_BROADWELL(dev_priv)) + return true; + return false; +} + +/** + * intel_gvt_init - initialize GVT components + * @dev_priv: drm i915 private data + * + * This function is called at the initialization stage to create a GVT device. + * + * Returns: + * Zero on success, negative error code if failed. + * + */ +int intel_gvt_init(struct drm_i915_private *dev_priv) +{ + int ret; + + if (!i915.enable_gvt) { + DRM_DEBUG_DRIVER("GVT-g is disabled by kernel params\n"); + return 0; + } + + if (!is_supported_device(dev_priv)) { + DRM_DEBUG_DRIVER("Unsupported device. GVT-g is disabled\n"); + return 0; + } + + /* + * We're not in host or fail to find a MPT module, disable GVT-g + */ + ret = intel_gvt_init_host(); + if (ret) { + DRM_DEBUG_DRIVER("Not in host or MPT modules not found\n"); + return 0; + } + + ret = intel_gvt_init_device(dev_priv); + if (ret) { + DRM_DEBUG_DRIVER("Fail to init GVT device\n"); + return 0; + } + + return 0; +} + +/** + * intel_gvt_cleanup - cleanup GVT components when i915 driver is unloading + * @dev_priv: drm i915 private * + * + * This function is called at the i915 driver unloading stage, to shutdown + * GVT components and release the related resources. + */ +void intel_gvt_cleanup(struct drm_i915_private *dev_priv) +{ + if (!intel_gvt_active(dev_priv)) + return; + + intel_gvt_clean_device(dev_priv); +} diff --git a/drivers/gpu/drm/i915/intel_gvt.h b/drivers/gpu/drm/i915/intel_gvt.h new file mode 100644 index 000000000000..960211df74db --- /dev/null +++ b/drivers/gpu/drm/i915/intel_gvt.h @@ -0,0 +1,45 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _INTEL_GVT_H_ +#define _INTEL_GVT_H_ + +#include "gvt/gvt.h" + +#ifdef CONFIG_DRM_I915_GVT +int intel_gvt_init(struct drm_i915_private *dev_priv); +void intel_gvt_cleanup(struct drm_i915_private *dev_priv); +int intel_gvt_init_device(struct drm_i915_private *dev_priv); +void intel_gvt_clean_device(struct drm_i915_private *dev_priv); +int intel_gvt_init_host(void); +#else +static inline int intel_gvt_init(struct drm_i915_private *dev_priv) +{ + return 0; +} +static inline void intel_gvt_cleanup(struct drm_i915_private *dev_priv) +{ +} +#endif + +#endif /* _INTEL_GVT_H_ */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 6b29da9bba38..fb21626ada64 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1774,6 +1774,7 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_hdmi_set_property, .atomic_get_property = intel_connector_atomic_get_property, + .early_unregister = intel_connector_unregister, .destroy = intel_hdmi_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -1809,6 +1810,9 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, enum port port = intel_dig_port->port; uint8_t alternate_ddc_pin; + DRM_DEBUG_KMS("Adding HDMI connector on port %c\n", + port_name(port)); + if (WARN(intel_dig_port->max_lanes < 4, "Not enough lanes (%d) for HDMI on port %c\n", intel_dig_port->max_lanes, port_name(port))) @@ -1906,7 +1910,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; - intel_connector->unregister = intel_connector_unregister; intel_hdmi_add_properties(intel_hdmi, connector); diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 5c191a1afaaf..debed011a958 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -208,16 +208,6 @@ } while (0) enum { - ADVANCED_CONTEXT = 0, - LEGACY_32B_CONTEXT, - ADVANCED_AD_CONTEXT, - LEGACY_64B_CONTEXT -}; -#define GEN8_CTX_ADDRESSING_MODE_SHIFT 3 -#define GEN8_CTX_ADDRESSING_MODE(dev) (USES_FULL_48BIT_PPGTT(dev) ?\ - LEGACY_64B_CONTEXT :\ - LEGACY_32B_CONTEXT) -enum { FAULT_AND_HANG = 0, FAULT_AND_HALT, /* Debug only */ FAULT_AND_STREAM, @@ -238,7 +228,7 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx, /** * intel_sanitize_enable_execlists() - sanitize i915.enable_execlists - * @dev: DRM device. + * @dev_priv: i915 device private * @enable_execlists: value of i915.enable_execlists module parameter. * * Only certain platforms support Execlists (the prerequisites being @@ -281,8 +271,6 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine) (engine->id == VCS || engine->id == VCS2); engine->ctx_desc_template = GEN8_CTX_VALID; - engine->ctx_desc_template |= GEN8_CTX_ADDRESSING_MODE(dev_priv) << - GEN8_CTX_ADDRESSING_MODE_SHIFT; if (IS_GEN8(dev_priv)) engine->ctx_desc_template |= GEN8_CTX_L3LLC_COHERENT; engine->ctx_desc_template |= GEN8_CTX_PRIVILEGE; @@ -325,7 +313,8 @@ intel_lr_context_descriptor_update(struct i915_gem_context *ctx, BUILD_BUG_ON(MAX_CONTEXT_HW_ID > (1<<GEN8_CTX_ID_WIDTH)); - desc = engine->ctx_desc_template; /* bits 0-11 */ + desc = ctx->desc_template; /* bits 3-4 */ + desc |= engine->ctx_desc_template; /* bits 0-11 */ desc |= ce->lrc_vma->node.start + LRC_PPHWSP_PN * PAGE_SIZE; /* bits 12-31 */ desc |= (u64)ctx->hw_id << GEN8_CTX_ID_SHIFT; /* bits 32-52 */ @@ -415,6 +404,20 @@ static void execlists_submit_requests(struct drm_i915_gem_request *rq0, spin_unlock_irq(&dev_priv->uncore.lock); } +static inline void execlists_context_status_change( + struct drm_i915_gem_request *rq, + unsigned long status) +{ + /* + * Only used when GVT-g is enabled now. When GVT-g is disabled, + * The compiler should eliminate this function as dead-code. + */ + if (!IS_ENABLED(CONFIG_DRM_I915_GVT)) + return; + + atomic_notifier_call_chain(&rq->ctx->status_notifier, status, rq); +} + static void execlists_context_unqueue(struct intel_engine_cs *engine) { struct drm_i915_gem_request *req0 = NULL, *req1 = NULL; @@ -441,6 +444,20 @@ static void execlists_context_unqueue(struct intel_engine_cs *engine) i915_gem_request_unreference(req0); req0 = cursor; } else { + if (IS_ENABLED(CONFIG_DRM_I915_GVT)) { + /* + * req0 (after merged) ctx requires single + * submission, stop picking + */ + if (req0->ctx->execlists_force_single_submission) + break; + /* + * req0 ctx doesn't require single submission, + * but next req ctx requires, stop picking + */ + if (cursor->ctx->execlists_force_single_submission) + break; + } req1 = cursor; WARN_ON(req1->elsp_submitted); break; @@ -450,6 +467,12 @@ static void execlists_context_unqueue(struct intel_engine_cs *engine) if (unlikely(!req0)) return; + execlists_context_status_change(req0, INTEL_CONTEXT_SCHEDULE_IN); + + if (req1) + execlists_context_status_change(req1, + INTEL_CONTEXT_SCHEDULE_IN); + if (req0->elsp_submitted & engine->idle_lite_restore_wa) { /* * WaIdleLiteRestore: make sure we never cause a lite restore @@ -488,6 +511,8 @@ execlists_check_remove_request(struct intel_engine_cs *engine, u32 ctx_id) if (--head_req->elsp_submitted > 0) return 0; + execlists_context_status_change(head_req, INTEL_CONTEXT_SCHEDULE_OUT); + list_del(&head_req->execlist_link); i915_gem_request_unreference(head_req); @@ -516,7 +541,7 @@ get_context_status(struct intel_engine_cs *engine, unsigned int read_pointer, /** * intel_lrc_irq_handler() - handle Context Switch interrupts - * @engine: Engine Command Streamer to handle. + * @data: tasklet handler passed in unsigned long * * Check the unread Context Status Buffers and manage the submission of new * contexts to the ELSP accordingly. @@ -786,15 +811,9 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) /** * execlists_submission() - submit a batchbuffer for execution, Execlists style - * @dev: DRM device. - * @file: DRM file. - * @ring: Engine Command Streamer to submit to. - * @ctx: Context to employ for this submission. + * @params: execbuffer call parameters. * @args: execbuffer call arguments. * @vmas: list of vmas. - * @batch_obj: the batchbuffer to submit. - * @exec_start: batchbuffer start virtual address pointer. - * @dispatch_flags: translated execbuffer call flags. * * This is the evil twin version of i915_gem_ringbuffer_submission. It abstracts * away the submission details of the execbuffer ioctl call. @@ -1081,12 +1100,13 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, uint32_t l3sqc4_flush = (0x40400000 | GEN8_LQSC_FLUSH_COHERENT_LINES); /* - * WaDisableLSQCROPERFforOCL:skl + * WaDisableLSQCROPERFforOCL:skl,kbl * This WA is implemented in skl_init_clock_gating() but since * this batch updates GEN8_L3SQCREG4 with default value we need to * set this bit here to retain the WA during flush. */ - if (IS_SKL_REVID(engine->i915, 0, SKL_REVID_E0)) + if (IS_SKL_REVID(engine->i915, 0, SKL_REVID_E0) || + IS_KBL_REVID(engine->i915, 0, KBL_REVID_E0)) l3sqc4_flush |= GEN8_LQSC_RO_PERF_DIS; wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 | @@ -1138,7 +1158,7 @@ static inline int wa_ctx_end(struct i915_wa_ctx_bb *wa_ctx, /** * gen8_init_indirectctx_bb() - initialize indirect ctx batch with WA * - * @ring: only applicable for RCS + * @engine: only applicable for RCS * @wa_ctx: structure representing wa_ctx * offset: specifies start of the batch, should be cache-aligned. This is updated * with the offset value received as input. @@ -1212,7 +1232,7 @@ static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine, /** * gen8_init_perctx_bb() - initialize per ctx batch with WA * - * @ring: only applicable for RCS + * @engine: only applicable for RCS * @wa_ctx: structure representing wa_ctx * offset: specifies start of the batch, should be cache-aligned. * size: size of the batch in DWORDS but HW expects in terms of cachelines @@ -1260,6 +1280,22 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine, return ret; index = ret; + /* WaClearSlmSpaceAtContextSwitch:kbl */ + /* Actual scratch location is at 128 bytes offset */ + if (IS_KBL_REVID(engine->i915, 0, KBL_REVID_A0)) { + uint32_t scratch_addr + = engine->scratch.gtt_offset + 2*CACHELINE_BYTES; + + wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6)); + wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 | + PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_QW_WRITE)); + wa_ctx_emit(batch, index, scratch_addr); + wa_ctx_emit(batch, index, 0); + wa_ctx_emit(batch, index, 0); + wa_ctx_emit(batch, index, 0); + } /* Pad to end of cacheline */ while (index % CACHELINE_DWORDS) wa_ctx_emit(batch, index, MI_NOOP); @@ -1657,9 +1693,10 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request, struct intel_ringbuffer *ringbuf = request->ringbuf; struct intel_engine_cs *engine = ringbuf->engine; u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES; - bool vf_flush_wa = false; + bool vf_flush_wa = false, dc_flush_wa = false; u32 flags = 0; int ret; + int len; flags |= PIPE_CONTROL_CS_STALL; @@ -1686,9 +1723,21 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request, */ if (IS_GEN9(request->i915)) vf_flush_wa = true; + + /* WaForGAMHang:kbl */ + if (IS_KBL_REVID(request->i915, 0, KBL_REVID_B0)) + dc_flush_wa = true; } - ret = intel_ring_begin(request, vf_flush_wa ? 12 : 6); + len = 6; + + if (vf_flush_wa) + len += 6; + + if (dc_flush_wa) + len += 12; + + ret = intel_ring_begin(request, len); if (ret) return ret; @@ -1701,12 +1750,31 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request, intel_logical_ring_emit(ringbuf, 0); } + if (dc_flush_wa) { + intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); + intel_logical_ring_emit(ringbuf, PIPE_CONTROL_DC_FLUSH_ENABLE); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + } + intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); intel_logical_ring_emit(ringbuf, flags); intel_logical_ring_emit(ringbuf, scratch_addr); intel_logical_ring_emit(ringbuf, 0); intel_logical_ring_emit(ringbuf, 0); intel_logical_ring_emit(ringbuf, 0); + + if (dc_flush_wa) { + intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); + intel_logical_ring_emit(ringbuf, PIPE_CONTROL_CS_STALL); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + } + intel_logical_ring_advance(ringbuf); return 0; @@ -1860,7 +1928,7 @@ static int gen8_init_rcs_context(struct drm_i915_gem_request *req) /** * intel_logical_ring_cleanup() - deallocate the Engine Command Streamer * - * @ring: Engine Command Streamer. + * @engine: Engine Command Streamer. * */ void intel_logical_ring_cleanup(struct intel_engine_cs *engine) @@ -2413,7 +2481,7 @@ populate_lr_context(struct i915_gem_context *ctx, /** * intel_lr_context_size() - return the size of the context for an engine - * @ring: which engine to find the context size for + * @engine: which engine to find the context size for * * Each engine may require a different amount of space for a context image, * so when allocating (or copying) an image, this function can be used to @@ -2484,7 +2552,7 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, return PTR_ERR(ctx_obj); } - ringbuf = intel_engine_create_ringbuffer(engine, 4 * PAGE_SIZE); + ringbuf = intel_engine_create_ringbuffer(engine, ctx->ring_size); if (IS_ERR(ringbuf)) { ret = PTR_ERR(ringbuf); goto error_deref_obj; diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index a8db42a9c50f..2b8255c19dcc 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -57,6 +57,11 @@ #define GEN8_CSB_READ_PTR(csb_status) \ (((csb_status) & GEN8_CSB_READ_PTR_MASK) >> 8) +enum { + INTEL_CONTEXT_SCHEDULE_IN = 0, + INTEL_CONTEXT_SCHEDULE_OUT, +}; + /* Logical Rings */ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request); int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e06b9036bebc..e9082185a375 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -555,6 +555,7 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_lvds_set_property, .atomic_get_property = intel_connector_atomic_get_property, + .early_unregister = intel_connector_unregister, .destroy = intel_lvds_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -991,7 +992,6 @@ void intel_lvds_init(struct drm_device *dev) intel_encoder->get_hw_state = intel_lvds_get_hw_state; intel_encoder->get_config = intel_lvds_get_config; intel_connector->get_hw_state = intel_connector_get_hw_state; - intel_connector->unregister = intel_connector_unregister; intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; diff --git a/drivers/gpu/drm/i915/intel_mocs.c b/drivers/gpu/drm/i915/intel_mocs.c index b765c75f3fcd..3c1482b8f2f4 100644 --- a/drivers/gpu/drm/i915/intel_mocs.c +++ b/drivers/gpu/drm/i915/intel_mocs.c @@ -156,6 +156,16 @@ static bool get_mocs_settings(struct drm_i915_private *dev_priv, "Platform that should have a MOCS table does not.\n"); } + /* WaDisableSkipCaching:skl,bxt,kbl */ + if (IS_GEN9(dev_priv)) { + int i; + + for (i = 0; i < table->size; i++) + if (WARN_ON(table->table[i].l3cc_value & + (L3_ESC(1) | L3_SCC(0x7)))) + return false; + } + return result; } diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index f0b1602c3258..bf721781c259 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -1216,7 +1216,7 @@ static int intel_backlight_device_register(struct intel_connector *connector) return 0; } -static void intel_backlight_device_unregister(struct intel_connector *connector) +void intel_backlight_device_unregister(struct intel_connector *connector) { struct intel_panel *panel = &connector->panel; @@ -1230,9 +1230,6 @@ static int intel_backlight_device_register(struct intel_connector *connector) { return 0; } -static void intel_backlight_device_unregister(struct intel_connector *connector) -{ -} #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ /* @@ -1820,11 +1817,3 @@ void intel_backlight_register(struct drm_device *dev) for_each_intel_connector(dev, connector) intel_backlight_device_register(connector); } - -void intel_backlight_unregister(struct drm_device *dev) -{ - struct intel_connector *connector; - - for_each_intel_connector(dev, connector) - intel_backlight_device_unregister(connector); -} diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 08274591db7e..658a75659657 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -55,14 +55,38 @@ #define INTEL_RC6p_ENABLE (1<<1) #define INTEL_RC6pp_ENABLE (1<<2) -static void bxt_init_clock_gating(struct drm_device *dev) +static void gen9_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - /* See Bspec note for PSR2_CTL bit 31, Wa#828:bxt */ + /* See Bspec note for PSR2_CTL bit 31, Wa#828:skl,bxt,kbl */ I915_WRITE(CHICKEN_PAR1_1, I915_READ(CHICKEN_PAR1_1) | SKL_EDP_PSR_FIX_RDWRAP); + I915_WRITE(GEN8_CONFIG0, + I915_READ(GEN8_CONFIG0) | GEN9_DEFAULT_FIXES); + + /* WaEnableChickenDCPR:skl,bxt,kbl */ + I915_WRITE(GEN8_CHICKEN_DCPR_1, + I915_READ(GEN8_CHICKEN_DCPR_1) | MASK_WAKEMEM); + + /* WaFbcTurnOffFbcWatermark:skl,bxt,kbl */ + /* WaFbcWakeMemOn:skl,bxt,kbl */ + I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | + DISP_FBC_WM_DIS | + DISP_FBC_MEMORY_WAKE); + + /* WaFbcHighMemBwCorruptionAvoidance:skl,bxt,kbl */ + I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | + ILK_DPFC_DISABLE_DUMMY0); +} + +static void bxt_init_clock_gating(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + gen9_init_clock_gating(dev); + /* WaDisableSDEUnitClockGating:bxt */ I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | GEN8_SDEUNIT_CLOCK_GATE_DISABLE); @@ -6963,13 +6987,40 @@ static void gen8_set_l3sqc_credits(struct drm_i915_private *dev_priv, I915_WRITE(GEN7_MISCCPCTL, misccpctl); } +static void kabylake_init_clock_gating(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + gen9_init_clock_gating(dev); + + /* WaDisableSDEUnitClockGating:kbl */ + if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0)) + I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | + GEN8_SDEUNIT_CLOCK_GATE_DISABLE); + + /* WaDisableGamClockGating:kbl */ + if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0)) + I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | + GEN6_GAMUNIT_CLOCK_GATE_DISABLE); + + /* WaFbcNukeOnHostModify:kbl */ + I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | + ILK_DPFC_NUKE_ON_ANY_MODIFICATION); +} + static void skylake_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - /* See Bspec note for PSR2_CTL bit 31, Wa#828:skl,kbl */ - I915_WRITE(CHICKEN_PAR1_1, - I915_READ(CHICKEN_PAR1_1) | SKL_EDP_PSR_FIX_RDWRAP); + gen9_init_clock_gating(dev); + + /* WAC6entrylatency:skl */ + I915_WRITE(FBC_LLC_READ_CTRL, I915_READ(FBC_LLC_READ_CTRL) | + FBC_LLC_FULLY_OPEN); + + /* WaFbcNukeOnHostModify:skl */ + I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | + ILK_DPFC_NUKE_ON_ANY_MODIFICATION); } static void broadwell_init_clock_gating(struct drm_device *dev) @@ -7016,6 +7067,10 @@ static void broadwell_init_clock_gating(struct drm_device *dev) */ I915_WRITE(HSW_GTT_CACHE_EN, GTT_CACHE_EN_ALL); + /* WaKVMNotificationOnConfigChange:bdw */ + I915_WRITE(CHICKEN_PAR2_1, I915_READ(CHICKEN_PAR2_1) + | KVM_CONFIG_CHANGE_NOTIFICATION_SELECT); + lpt_init_clock_gating(dev); } @@ -7433,7 +7488,7 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) if (IS_SKYLAKE(dev_priv)) dev_priv->display.init_clock_gating = skylake_init_clock_gating; else if (IS_KABYLAKE(dev_priv)) - dev_priv->display.init_clock_gating = skylake_init_clock_gating; + dev_priv->display.init_clock_gating = kabylake_init_clock_gating; else if (IS_BROXTON(dev_priv)) dev_priv->display.init_clock_gating = bxt_init_clock_gating; else if (IS_BROADWELL(dev_priv)) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 8d35a3978f9b..fedd27049814 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -908,24 +908,26 @@ static int chv_init_workarounds(struct intel_engine_cs *engine) static int gen9_init_workarounds(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - uint32_t tmp; int ret; - /* WaEnableLbsSlaRetryTimerDecrement:skl */ + /* WaConextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl */ + I915_WRITE(GEN9_CSFE_CHICKEN1_RCS, _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE)); + + /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl */ I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) | GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE); - /* WaDisableKillLogic:bxt,skl */ + /* WaDisableKillLogic:bxt,skl,kbl */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | ECOCHK_DIS_TLB); - /* WaClearFlowControlGpgpuContextSave:skl,bxt */ - /* WaDisablePartialInstShootdown:skl,bxt */ + /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl */ + /* WaDisablePartialInstShootdown:skl,bxt,kbl */ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, FLOW_CONTROL_ENABLE | PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE); - /* Syncing dependencies between camera and graphics:skl,bxt */ + /* Syncing dependencies between camera and graphics:skl,bxt,kbl */ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3, GEN9_DISABLE_OCL_OOB_SUPPRESS_LOGIC); @@ -947,18 +949,18 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine) */ } - /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt */ - /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt */ + /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl */ + /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl */ WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7, GEN9_ENABLE_YV12_BUGFIX | GEN9_ENABLE_GPGPU_PREEMPTION); - /* Wa4x4STCOptimizationDisable:skl,bxt */ - /* WaDisablePartialResolveInVc:skl,bxt */ + /* Wa4x4STCOptimizationDisable:skl,bxt,kbl */ + /* WaDisablePartialResolveInVc:skl,bxt,kbl */ WA_SET_BIT_MASKED(CACHE_MODE_1, (GEN8_4x4_STC_OPTIMIZATION_DISABLE | GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE)); - /* WaCcsTlbPrefetchDisable:skl,bxt */ + /* WaCcsTlbPrefetchDisable:skl,bxt,kbl */ WA_CLR_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN5, GEN9_CCS_TLB_PREFETCH_ENABLE); @@ -968,31 +970,57 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine) WA_SET_BIT_MASKED(SLICE_ECO_CHICKEN0, PIXEL_MASK_CAMMING_DISABLE); - /* WaForceContextSaveRestoreNonCoherent:skl,bxt */ - tmp = HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT; - if (IS_SKL_REVID(dev_priv, SKL_REVID_F0, REVID_FOREVER) || - IS_BXT_REVID(dev_priv, BXT_REVID_B0, REVID_FOREVER)) - tmp |= HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE; - WA_SET_BIT_MASKED(HDC_CHICKEN0, tmp); + /* WaForceContextSaveRestoreNonCoherent:skl,bxt,kbl */ + WA_SET_BIT_MASKED(HDC_CHICKEN0, + HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT | + HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE); + + /* WaForceEnableNonCoherent and WaDisableHDCInvalidation are + * both tied to WaForceContextSaveRestoreNonCoherent + * in some hsds for skl. We keep the tie for all gen9. The + * documentation is a bit hazy and so we want to get common behaviour, + * even though there is no clear evidence we would need both on kbl/bxt. + * This area has been source of system hangs so we play it safe + * and mimic the skl regardless of what bspec says. + * + * Use Force Non-Coherent whenever executing a 3D context. This + * is a workaround for a possible hang in the unlikely event + * a TLB invalidation occurs during a PSD flush. + */ - /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt */ - if (IS_SKYLAKE(dev_priv) || IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0)) + /* WaForceEnableNonCoherent:skl,bxt,kbl */ + WA_SET_BIT_MASKED(HDC_CHICKEN0, + HDC_FORCE_NON_COHERENT); + + /* WaDisableHDCInvalidation:skl,bxt,kbl */ + I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | + BDW_DISABLE_HDC_INVALIDATION); + + /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl */ + if (IS_SKYLAKE(dev_priv) || + IS_KABYLAKE(dev_priv) || + IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0)) WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3, GEN8_SAMPLER_POWER_BYPASS_DIS); - /* WaDisableSTUnitPowerOptimization:skl,bxt */ + /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl */ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE); - /* WaOCLCoherentLineFlush:skl,bxt */ + /* WaOCLCoherentLineFlush:skl,bxt,kbl */ I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) | GEN8_LQSC_FLUSH_COHERENT_LINES)); - /* WaEnablePreemptionGranularityControlByUMD:skl,bxt */ + /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt */ + ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG); + if (ret) + return ret; + + /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl */ ret= wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1); if (ret) return ret; - /* WaAllowUMDToModifyHDCChicken1:skl,bxt */ + /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl */ ret = wa_ring_whitelist_reg(engine, GEN8_HDC_CHICKEN1); if (ret) return ret; @@ -1060,7 +1088,7 @@ static int skl_init_workarounds(struct intel_engine_cs *engine) _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL)); } - if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_D0)) { + if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_E0)) { /* WaDisableChickenBitTSGBarrierAckForFFSliceCS:skl */ I915_WRITE(FF_SLICE_CS_CHICKEN2, _MASKED_BIT_ENABLE(GEN9_TSG_BARRIER_ACK_DISABLE)); @@ -1085,22 +1113,6 @@ static int skl_init_workarounds(struct intel_engine_cs *engine) WA_SET_BIT_MASKED(HIZ_CHICKEN, BDW_HIZ_POWER_COMPILER_CLOCK_GATING_DISABLE); - /* This is tied to WaForceContextSaveRestoreNonCoherent */ - if (IS_SKL_REVID(dev_priv, 0, REVID_FOREVER)) { - /* - *Use Force Non-Coherent whenever executing a 3D context. This - * is a workaround for a possible hang in the unlikely event - * a TLB invalidation occurs during a PSD flush. - */ - /* WaForceEnableNonCoherent:skl */ - WA_SET_BIT_MASKED(HDC_CHICKEN0, - HDC_FORCE_NON_COHERENT); - - /* WaDisableHDCInvalidation:skl */ - I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | - BDW_DISABLE_HDC_INVALIDATION); - } - /* WaBarrierPerformanceFixDisable:skl */ if (IS_SKL_REVID(dev_priv, SKL_REVID_C0, SKL_REVID_D0)) WA_SET_BIT_MASKED(HDC_CHICKEN0, @@ -1113,6 +1125,9 @@ static int skl_init_workarounds(struct intel_engine_cs *engine) GEN7_HALF_SLICE_CHICKEN1, GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE); + /* WaDisableGafsUnitClkGating:skl */ + WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE); + /* WaDisableLSQCROPERFforOCL:skl */ ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4); if (ret) @@ -1145,6 +1160,12 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine) WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE); + /* WaDisablePooledEuLoadBalancingFix:bxt */ + if (IS_BXT_REVID(dev_priv, BXT_REVID_B0, REVID_FOREVER)) { + WA_SET_BIT_MASKED(FF_SLICE_CS_CHICKEN2, + GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE); + } + /* WaDisableSbeCacheDispatchPortSharing:bxt */ if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0)) { WA_SET_BIT_MASKED( @@ -1171,6 +1192,63 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine) I915_WRITE(GEN8_L3SQCREG1, L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2)); + /* WaInsertDummyPushConstPs:bxt */ + if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0)) + WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2, + GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION); + + return 0; +} + +static int kbl_init_workarounds(struct intel_engine_cs *engine) +{ + struct drm_i915_private *dev_priv = engine->i915; + int ret; + + ret = gen9_init_workarounds(engine); + if (ret) + return ret; + + /* WaEnableGapsTsvCreditFix:kbl */ + I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) | + GEN9_GAPS_TSV_CREDIT_DISABLE)); + + /* WaDisableDynamicCreditSharing:kbl */ + if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0)) + WA_SET_BIT(GAMT_CHKN_BIT_REG, + GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING); + + /* WaDisableFenceDestinationToSLM:kbl (pre-prod) */ + if (IS_KBL_REVID(dev_priv, KBL_REVID_A0, KBL_REVID_A0)) + WA_SET_BIT_MASKED(HDC_CHICKEN0, + HDC_FENCE_DEST_SLM_DISABLE); + + /* GEN8_L3SQCREG4 has a dependency with WA batch so any new changes + * involving this register should also be added to WA batch as required. + */ + if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0)) + /* WaDisableLSQCROPERFforOCL:kbl */ + I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) | + GEN8_LQSC_RO_PERF_DIS); + + /* WaInsertDummyPushConstPs:kbl */ + if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0)) + WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2, + GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION); + + /* WaDisableGafsUnitClkGating:kbl */ + WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE); + + /* WaDisableSbeCacheDispatchPortSharing:kbl */ + WA_SET_BIT_MASKED( + GEN7_HALF_SLICE_CHICKEN1, + GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE); + + /* WaDisableLSQCROPERFforOCL:kbl */ + ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4); + if (ret) + return ret; + return 0; } @@ -1195,6 +1273,9 @@ int init_workarounds_ring(struct intel_engine_cs *engine) if (IS_BROXTON(dev_priv)) return bxt_init_workarounds(engine); + if (IS_KABYLAKE(dev_priv)) + return kbl_init_workarounds(engine); + return 0; } diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index fe8faf30bda7..e856d49d6dc3 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -65,6 +65,9 @@ bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, int power_well_id); +static struct i915_power_well * +lookup_power_well(struct drm_i915_private *dev_priv, int power_well_id); + const char * intel_display_power_domain_str(enum intel_display_power_domain domain) { @@ -151,6 +154,23 @@ static void intel_power_well_disable(struct drm_i915_private *dev_priv, power_well->ops->disable(dev_priv, power_well); } +static void intel_power_well_get(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + if (!power_well->count++) + intel_power_well_enable(dev_priv, power_well); +} + +static void intel_power_well_put(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + WARN(!power_well->count, "Use count on power well %s is already zero", + power_well->name); + + if (!--power_well->count) + intel_power_well_disable(dev_priv, power_well); +} + /* * We should only use the power well if we explicitly asked the hardware to * enable it, so check if it's enabled and also check if we've requested it to @@ -419,6 +439,16 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv, BIT(POWER_DOMAIN_MODESET) | \ BIT(POWER_DOMAIN_AUX_A) | \ BIT(POWER_DOMAIN_INIT)) +#define BXT_DPIO_CMN_A_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_A_LANES) | \ + BIT(POWER_DOMAIN_AUX_A) | \ + BIT(POWER_DOMAIN_INIT)) +#define BXT_DPIO_CMN_BC_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \ + BIT(POWER_DOMAIN_AUX_B) | \ + BIT(POWER_DOMAIN_AUX_C) | \ + BIT(POWER_DOMAIN_INIT)) static void assert_can_enable_dc9(struct drm_i915_private *dev_priv) { @@ -800,6 +830,72 @@ static void skl_power_well_disable(struct drm_i915_private *dev_priv, skl_set_power_well(dev_priv, power_well, false); } +static enum dpio_phy bxt_power_well_to_phy(struct i915_power_well *power_well) +{ + enum skl_disp_power_wells power_well_id = power_well->data; + + return power_well_id == BXT_DPIO_CMN_A ? DPIO_PHY1 : DPIO_PHY0; +} + +static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum skl_disp_power_wells power_well_id = power_well->data; + struct i915_power_well *cmn_a_well; + + if (power_well_id == BXT_DPIO_CMN_BC) { + /* + * We need to copy the GRC calibration value from the eDP PHY, + * so make sure it's powered up. + */ + cmn_a_well = lookup_power_well(dev_priv, BXT_DPIO_CMN_A); + intel_power_well_get(dev_priv, cmn_a_well); + } + + bxt_ddi_phy_init(dev_priv, bxt_power_well_to_phy(power_well)); + + if (power_well_id == BXT_DPIO_CMN_BC) + intel_power_well_put(dev_priv, cmn_a_well); +} + +static void bxt_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + bxt_ddi_phy_uninit(dev_priv, bxt_power_well_to_phy(power_well)); +} + +static bool bxt_dpio_cmn_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + return bxt_ddi_phy_is_enabled(dev_priv, + bxt_power_well_to_phy(power_well)); +} + +static void bxt_dpio_cmn_power_well_sync_hw(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + if (power_well->count > 0) + bxt_dpio_cmn_power_well_enable(dev_priv, power_well); + else + bxt_dpio_cmn_power_well_disable(dev_priv, power_well); +} + + +static void bxt_verify_ddi_phy_power_wells(struct drm_i915_private *dev_priv) +{ + struct i915_power_well *power_well; + + power_well = lookup_power_well(dev_priv, BXT_DPIO_CMN_A); + if (power_well->count > 0) + bxt_ddi_phy_verify_state(dev_priv, + bxt_power_well_to_phy(power_well)); + + power_well = lookup_power_well(dev_priv, BXT_DPIO_CMN_BC); + if (power_well->count > 0) + bxt_ddi_phy_verify_state(dev_priv, + bxt_power_well_to_phy(power_well)); +} + static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { @@ -826,7 +922,7 @@ static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv, gen9_assert_dbuf_enabled(dev_priv); if (IS_BROXTON(dev_priv)) - broxton_ddi_phy_verify_state(dev_priv); + bxt_verify_ddi_phy_power_wells(dev_priv); } static void gen9_dc_off_power_well_disable(struct drm_i915_private *dev_priv, @@ -1518,10 +1614,8 @@ __intel_display_power_get_domain(struct drm_i915_private *dev_priv, struct i915_power_well *power_well; int i; - for_each_power_well(i, power_well, BIT(domain), power_domains) { - if (!power_well->count++) - intel_power_well_enable(dev_priv, power_well); - } + for_each_power_well(i, power_well, BIT(domain), power_domains) + intel_power_well_get(dev_priv, power_well); power_domains->domain_use_count[domain]++; } @@ -1615,14 +1709,8 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, intel_display_power_domain_str(domain)); power_domains->domain_use_count[domain]--; - for_each_power_well_rev(i, power_well, BIT(domain), power_domains) { - WARN(!power_well->count, - "Use count on power well %s is already zero", - power_well->name); - - if (!--power_well->count) - intel_power_well_disable(dev_priv, power_well); - } + for_each_power_well_rev(i, power_well, BIT(domain), power_domains) + intel_power_well_put(dev_priv, power_well); mutex_unlock(&power_domains->lock); @@ -1793,6 +1881,13 @@ static const struct i915_power_well_ops gen9_dc_off_power_well_ops = { .is_enabled = gen9_dc_off_power_well_enabled, }; +static const struct i915_power_well_ops bxt_dpio_cmn_power_well_ops = { + .sync_hw = bxt_dpio_cmn_power_well_sync_hw, + .enable = bxt_dpio_cmn_power_well_enable, + .disable = bxt_dpio_cmn_power_well_disable, + .is_enabled = bxt_dpio_cmn_power_well_enabled, +}; + static struct i915_power_well hsw_power_wells[] = { { .name = "always-on", @@ -2029,6 +2124,18 @@ static struct i915_power_well bxt_power_wells[] = { .ops = &skl_power_well_ops, .data = SKL_DISP_PW_2, }, + { + .name = "dpio-common-a", + .domains = BXT_DPIO_CMN_A_POWER_DOMAINS, + .ops = &bxt_dpio_cmn_power_well_ops, + .data = BXT_DPIO_CMN_A, + }, + { + .name = "dpio-common-bc", + .domains = BXT_DPIO_CMN_BC_POWER_DOMAINS, + .ops = &bxt_dpio_cmn_power_well_ops, + .data = BXT_DPIO_CMN_BC, + }, }; static int @@ -2294,14 +2401,10 @@ void bxt_display_core_init(struct drm_i915_private *dev_priv, mutex_unlock(&power_domains->lock); - broxton_init_cdclk(dev_priv); + bxt_init_cdclk(dev_priv); gen9_dbuf_enable(dev_priv); - broxton_ddi_phy_init(dev_priv); - - broxton_ddi_phy_verify_state(dev_priv); - if (resume && dev_priv->csr.dmc_payload) intel_csr_load_program(dev_priv); } @@ -2313,11 +2416,9 @@ void bxt_display_core_uninit(struct drm_i915_private *dev_priv) gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); - broxton_ddi_phy_uninit(dev_priv); - gen9_dbuf_disable(dev_priv); - broxton_uninit_cdclk(dev_priv); + bxt_uninit_cdclk(dev_priv); /* The spec doesn't call for removing the reset handshake flag */ @@ -2448,6 +2549,7 @@ static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) /** * intel_power_domains_init_hw - initialize hardware power domain state * @dev_priv: i915 device instance + * @resume: Called from resume code paths or not * * This function initializes the hardware power domain state and enables all * power domains using intel_display_set_init_power(). diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index ab2d0658abe6..02b4a6695528 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2177,12 +2177,23 @@ done: #undef CHECK_PROPERTY } +static void +intel_sdvo_connector_unregister(struct drm_connector *connector) +{ + struct intel_sdvo *sdvo = intel_attached_sdvo(connector); + + sysfs_remove_link(&connector->kdev->kobj, + sdvo->ddc.dev.kobj.name); + intel_connector_unregister(connector); +} + static const struct drm_connector_funcs intel_sdvo_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = intel_sdvo_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_sdvo_set_property, .atomic_get_property = intel_connector_atomic_get_property, + .early_unregister = intel_sdvo_connector_unregister, .destroy = intel_sdvo_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -2345,20 +2356,6 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, struct intel_sdvo *sdvo) return 0x72; } -static void -intel_sdvo_connector_unregister(struct intel_connector *intel_connector) -{ - struct drm_connector *drm_connector; - struct intel_sdvo *sdvo_encoder; - - drm_connector = &intel_connector->base; - sdvo_encoder = intel_attached_sdvo(&intel_connector->base); - - sysfs_remove_link(&drm_connector->kdev->kobj, - sdvo_encoder->ddc.dev.kobj.name); - intel_connector_unregister(intel_connector); -} - static int intel_sdvo_connector_init(struct intel_sdvo_connector *connector, struct intel_sdvo *encoder) @@ -2381,7 +2378,6 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, connector->base.base.doublescan_allowed = 0; connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; connector->base.get_hw_state = intel_sdvo_connector_get_hw_state; - connector->base.unregister = intel_sdvo_connector_unregister; intel_connector_attach_encoder(&connector->base, &encoder->base); ret = drm_connector_register(drm_connector); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 324ccb06397d..fc654173c491 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -166,6 +166,20 @@ void intel_pipe_update_end(struct intel_crtc *crtc, struct intel_flip_work *work trace_i915_pipe_update_end(crtc, end_vbl_count, scanline_end); + /* We're still in the vblank-evade critical section, this can't race. + * Would be slightly nice to just grab the vblank count and arm the + * event outside of the critical section - the spinlock might spin for a + * while ... */ + if (crtc->base.state->event) { + WARN_ON(drm_crtc_vblank_get(&crtc->base) != 0); + + spin_lock(&crtc->base.dev->event_lock); + drm_crtc_arm_vblank_event(&crtc->base, crtc->base.state->event); + spin_unlock(&crtc->base.dev->event_lock); + + crtc->base.state->event = NULL; + } + local_irq_enable(); if (crtc->debug.start_vbl_count && diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 7ac9e9b0e2c3..4ce70a9f9df2 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1501,6 +1501,7 @@ out: static const struct drm_connector_funcs intel_tv_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = intel_tv_detect, + .early_unregister = intel_connector_unregister, .destroy = intel_tv_destroy, .set_property = intel_tv_set_property, .atomic_get_property = intel_connector_atomic_get_property, @@ -1599,7 +1600,6 @@ intel_tv_init(struct drm_device *dev) intel_encoder->disable = intel_disable_tv; intel_encoder->get_hw_state = intel_tv_get_hw_state; intel_connector->get_hw_state = intel_connector_get_hw_state; - intel_connector->unregister = intel_connector_unregister; intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_TVOUT; diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 82656654fb21..7746418a4c08 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -407,7 +407,6 @@ static struct drm_driver imx_drm_driver = { .load = imx_drm_driver_load, .unload = imx_drm_driver_unload, .lastclose = imx_drm_driver_lastclose, - .set_busid = drm_platform_set_busid, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig index eeefc971801a..9eefecedc3da 100644 --- a/drivers/gpu/drm/mediatek/Kconfig +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -6,7 +6,6 @@ config DRM_MEDIATEK select DRM_KMS_HELPER select DRM_MIPI_DSI select DRM_PANEL - select IOMMU_DMA select MEMORY select MTK_SMI help diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index c33bf98c5d5e..eebb7d881c2b 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -280,8 +280,6 @@ static int mtk_drm_bind(struct device *dev) if (!drm) return -ENOMEM; - drm_dev_set_unique(drm, dev_name(dev)); - drm->dev_private = private; private->drm = drm; @@ -293,14 +291,8 @@ static int mtk_drm_bind(struct device *dev) if (ret < 0) goto err_deinit; - ret = drm_connector_register_all(drm); - if (ret < 0) - goto err_unregister; - return 0; -err_unregister: - drm_dev_unregister(drm); err_deinit: mtk_drm_kms_deinit(drm); err_free: @@ -455,7 +447,6 @@ static int mtk_drm_remove(struct platform_device *pdev) struct drm_device *drm = private->drm; int i; - drm_connector_unregister_all(drm); drm_dev_unregister(drm); mtk_drm_kms_deinit(drm); drm_dev_unref(drm); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c index 51bc8988fc26..3995765a90dc 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -170,6 +170,7 @@ static int mtk_plane_atomic_check(struct drm_plane *plane, return drm_plane_helper_check_update(plane, state->crtc, fb, &src, &dest, &clip, + state->rotation, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, true, true, &visible); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9c654092ef78..a02dc2b27739 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -197,8 +197,6 @@ static int msm_drm_uninit(struct device *dev) drm_kms_helper_poll_fini(ddev); - drm_connector_unregister_all(ddev); - drm_dev_unregister(ddev); #ifdef CONFIG_DRM_FBDEV_EMULATION @@ -431,12 +429,6 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) if (ret) goto fail; - ret = drm_connector_register_all(ddev); - if (ret) { - dev_err(dev, "failed to register connectors\n"); - goto fail; - } - drm_mode_config_reset(ddev); #ifdef CONFIG_DRM_FBDEV_EMULATION @@ -730,7 +722,6 @@ static struct drm_driver msm_driver = { .open = msm_open, .preclose = msm_preclose, .lastclose = msm_lastclose, - .set_busid = drm_platform_set_busid, .irq_handler = msm_irq, .irq_preinstall = msm_irq_preinstall, .irq_postinstall = msm_irq_postinstall, diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index c00ff6e786a3..295e7621cc68 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1070,7 +1070,6 @@ nouveau_drm_init(void) driver_pci = driver_stub; driver_pci.set_busid = drm_pci_set_busid; driver_platform = driver_stub; - driver_platform.set_busid = drm_platform_set_busid; nouveau_display_options(); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 6b97011154bf..26c6134eb744 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -801,7 +801,6 @@ static struct drm_driver omap_drm_driver = { .unload = dev_unload, .open = dev_open, .lastclose = dev_lastclose, - .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = omap_irq_enable_vblank, .disable_vblank = omap_irq_disable_vblank, diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 31dfa0893416..adb10fbe918d 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -279,9 +279,6 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) if (ret) goto fini; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - ret = drm_fb_helper_initial_config(helper, 32); if (ret) goto fini; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index c5b214cc94e1..899ef7a2a7b4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -278,7 +278,6 @@ static int rcar_du_remove(struct platform_device *pdev) struct rcar_du_device *rcdu = platform_get_drvdata(pdev); struct drm_device *ddev = rcdu->ddev; - drm_connector_unregister_all(ddev); drm_dev_unregister(ddev); if (rcdu->fbdev) @@ -320,8 +319,6 @@ static int rcar_du_probe(struct platform_device *pdev) if (!ddev) return -ENOMEM; - drm_dev_set_unique(ddev, dev_name(&pdev->dev)); - rcdu->ddev = ddev; ddev->dev_private = rcdu; @@ -360,10 +357,6 @@ static int rcar_du_probe(struct platform_device *pdev) if (ret) goto error; - ret = drm_connector_register_all(ddev); - if (ret < 0) - goto error; - DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); return 0; diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index d30bdc38a760..e48611e83c03 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -2,6 +2,7 @@ config DRM_ROCKCHIP tristate "DRM Support for Rockchip" depends on DRM && ROCKCHIP_IOMMU depends on RESET_CONTROLLER + select DRM_GEM_CMA_HELPER select DRM_KMS_HELPER select DRM_KMS_FB_HELPER select DRM_PANEL diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index c2bcc5ea1abe..d665fb04d264 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -146,16 +146,12 @@ static int rockchip_drm_bind(struct device *dev) if (!drm_dev) return -ENOMEM; - ret = drm_dev_register(drm_dev, 0); - if (ret) - goto err_free; - dev_set_drvdata(dev, drm_dev); private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); if (!private) { ret = -ENOMEM; - goto err_unregister; + goto err_free; } drm_dev->dev_private = private; @@ -197,12 +193,6 @@ static int rockchip_drm_bind(struct device *dev) if (ret) goto err_detach_device; - ret = drm_connector_register_all(drm_dev); - if (ret) { - dev_err(dev, "failed to register connectors\n"); - goto err_unbind; - } - /* init kms poll for handling hpd */ drm_kms_helper_poll_init(drm_dev); @@ -222,14 +212,19 @@ static int rockchip_drm_bind(struct device *dev) if (ret) goto err_vblank_cleanup; + ret = drm_dev_register(drm_dev, 0); + if (ret) + goto err_fbdev_fini; + if (is_support_iommu) arm_iommu_release_mapping(mapping); return 0; +err_fbdev_fini: + rockchip_drm_fbdev_fini(drm_dev); err_vblank_cleanup: drm_vblank_cleanup(drm_dev); err_kms_helper_poll_fini: drm_kms_helper_poll_fini(drm_dev); -err_unbind: component_unbind_all(dev, drm_dev); err_detach_device: if (is_support_iommu) @@ -240,8 +235,6 @@ err_release_mapping: err_config_cleanup: drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; -err_unregister: - drm_dev_unregister(drm_dev); err_free: drm_dev_unref(drm_dev); return ret; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 8cd840f602b7..6255e5bcd954 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -626,6 +626,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, ret = drm_plane_helper_check_update(plane, crtc, state->fb, src, dest, &clip, + state->rotation, min_scale, max_scale, true, true, &visible); diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index ee79264b5b6a..f0492603ea88 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -259,7 +259,6 @@ static struct drm_driver shmob_drm_driver = { | DRIVER_PRIME, .load = shmob_drm_load, .unload = shmob_drm_unload, - .set_busid = drm_platform_set_busid, .irq_handler = shmob_drm_irq, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = shmob_drm_enable_vblank, diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 93ad8a5704d1..03defda77766 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -316,7 +316,7 @@ void sis_reclaim_buffers_locked(struct drm_device *dev, struct sis_file_private *file_priv = file->driver_priv; struct sis_memblock *entry, *next; - if (!(file->minor->master && file->master->lock.hw_lock)) + if (!(dev->master && file->master->lock.hw_lock)) return; drm_legacy_idlelock_take(&file->master->lock); diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index 3d2fa3ab33df..794148ff0e57 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -55,6 +55,26 @@ struct sti_compositor_data stih416_compositor_data = { }, }; +int sti_compositor_debufs_init(struct sti_compositor *compo, + struct drm_minor *minor) +{ + int ret = 0, i; + + for (i = 0; compo->vid[i]; i++) { + ret = vid_debugfs_init(compo->vid[i], minor); + if (ret) + return ret; + } + + for (i = 0; compo->mixer[i]; i++) { + ret = sti_mixer_debugfs_init(compo->mixer[i], minor); + if (ret) + return ret; + } + + return 0; +} + static int sti_compositor_bind(struct device *dev, struct device *master, void *data) diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h index 1a4a73dab11e..24444ef42a98 100644 --- a/drivers/gpu/drm/sti/sti_compositor.h +++ b/drivers/gpu/drm/sti/sti_compositor.h @@ -81,4 +81,7 @@ struct sti_compositor { struct notifier_block vtg_vblank_nb; }; +int sti_compositor_debufs_init(struct sti_compositor *compo, + struct drm_minor *minor); + #endif diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index e04deedabd4a..7fab3af7473b 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -331,6 +331,17 @@ void sti_crtc_disable_vblank(struct drm_device *drm_dev, unsigned int pipe) } } +static int sti_crtc_late_register(struct drm_crtc *crtc) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + struct sti_compositor *compo = dev_get_drvdata(mixer->dev); + + if (drm_crtc_index(crtc) == 0) + return sti_compositor_debufs_init(compo, crtc->dev->primary); + + return 0; +} + static const struct drm_crtc_funcs sti_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -339,6 +350,7 @@ static const struct drm_crtc_funcs sti_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .late_register = sti_crtc_late_register, }; bool sti_crtc_is_main(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index 53aa0029295b..a263bbba4119 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -329,6 +329,33 @@ static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = { .atomic_disable = sti_cursor_atomic_disable, }; +static void sti_cursor_destroy(struct drm_plane *drm_plane) +{ + DRM_DEBUG_DRIVER("\n"); + + drm_plane_helper_disable(drm_plane); + drm_plane_cleanup(drm_plane); +} + +static int sti_cursor_late_register(struct drm_plane *drm_plane) +{ + struct sti_plane *plane = to_sti_plane(drm_plane); + struct sti_cursor *cursor = to_sti_cursor(plane); + + return cursor_debugfs_init(cursor, drm_plane->dev->primary); +} + +struct drm_plane_funcs sti_cursor_plane_helpers_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = sti_cursor_destroy, + .set_property = sti_plane_set_property, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .late_register = sti_cursor_late_register, +}; + struct drm_plane *sti_cursor_create(struct drm_device *drm_dev, struct device *dev, int desc, void __iomem *baseaddr, @@ -363,7 +390,7 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev, res = drm_universal_plane_init(drm_dev, &cursor->plane.drm_plane, possible_crtcs, - &sti_plane_helpers_funcs, + &sti_cursor_plane_helpers_funcs, cursor_supported_formats, ARRAY_SIZE(cursor_supported_formats), DRM_PLANE_TYPE_CURSOR, NULL); @@ -377,9 +404,6 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev, sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR); - if (cursor_debugfs_init(cursor, drm_dev->primary)) - DRM_ERROR("CURSOR debugfs setup failed\n"); - return &cursor->plane.drm_plane; err_plane: diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index dd2c400c4a46..96bd3d08b2d4 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -226,8 +226,28 @@ static int sti_atomic_commit(struct drm_device *drm, return 0; } +static void sti_output_poll_changed(struct drm_device *ddev) +{ + struct sti_private *private = ddev->dev_private; + + if (!ddev->mode_config.num_connector) + return; + + if (private->fbdev) { + drm_fbdev_cma_hotplug_event(private->fbdev); + return; + } + + private->fbdev = drm_fbdev_cma_init(ddev, 32, + ddev->mode_config.num_crtc, + ddev->mode_config.num_connector); + if (IS_ERR(private->fbdev)) + private->fbdev = NULL; +} + static const struct drm_mode_config_funcs sti_mode_config_funcs = { .fb_create = drm_fb_cma_create, + .output_poll_changed = sti_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = sti_atomic_commit, }; @@ -248,45 +268,6 @@ static void sti_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &sti_mode_config_funcs; } -static int sti_load(struct drm_device *dev, unsigned long flags) -{ - struct sti_private *private; - int ret; - - private = kzalloc(sizeof(*private), GFP_KERNEL); - if (!private) { - DRM_ERROR("Failed to allocate private\n"); - return -ENOMEM; - } - dev->dev_private = (void *)private; - private->drm_dev = dev; - - mutex_init(&private->commit.lock); - INIT_WORK(&private->commit.work, sti_atomic_work); - - drm_mode_config_init(dev); - drm_kms_helper_poll_init(dev); - - sti_mode_config_init(dev); - - ret = component_bind_all(dev->dev, dev); - if (ret) { - drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); - kfree(private); - return ret; - } - - drm_mode_config_reset(dev); - - drm_helper_disable_unused_functions(dev); - drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_crtc, - dev->mode_config.num_connector); - - return 0; -} - static const struct file_operations sti_driver_fops = { .owner = THIS_MODULE, .open = drm_open, @@ -303,7 +284,6 @@ static const struct file_operations sti_driver_fops = { static struct drm_driver sti_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .load = sti_load, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, @@ -340,14 +320,88 @@ static int compare_of(struct device *dev, void *data) return dev->of_node == data; } +static int sti_init(struct drm_device *ddev) +{ + struct sti_private *private; + + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + ddev->dev_private = (void *)private; + dev_set_drvdata(ddev->dev, ddev); + private->drm_dev = ddev; + + mutex_init(&private->commit.lock); + INIT_WORK(&private->commit.work, sti_atomic_work); + + drm_mode_config_init(ddev); + + sti_mode_config_init(ddev); + + drm_kms_helper_poll_init(ddev); + + return 0; +} + +static void sti_cleanup(struct drm_device *ddev) +{ + struct sti_private *private = ddev->dev_private; + + if (private->fbdev) { + drm_fbdev_cma_fini(private->fbdev); + private->fbdev = NULL; + } + + drm_kms_helper_poll_fini(ddev); + drm_vblank_cleanup(ddev); + kfree(private); + ddev->dev_private = NULL; +} + static int sti_bind(struct device *dev) { - return drm_platform_init(&sti_driver, to_platform_device(dev)); + struct drm_device *ddev; + int ret; + + ddev = drm_dev_alloc(&sti_driver, dev); + if (!ddev) + return -ENOMEM; + + ddev->platformdev = to_platform_device(dev); + + ret = sti_init(ddev); + if (ret) + goto err_drm_dev_unref; + + ret = component_bind_all(ddev->dev, ddev); + if (ret) + goto err_cleanup; + + ret = drm_dev_register(ddev, 0); + if (ret) + goto err_register; + + drm_mode_config_reset(ddev); + + return 0; + +err_register: + drm_mode_config_cleanup(ddev); +err_cleanup: + sti_cleanup(ddev); +err_drm_dev_unref: + drm_dev_unref(ddev); + return ret; } static void sti_unbind(struct device *dev) { - drm_put_dev(dev_get_drvdata(dev)); + struct drm_device *ddev = dev_get_drvdata(dev); + + drm_dev_unregister(ddev); + sti_cleanup(ddev); + drm_dev_unref(ddev); } static const struct component_master_ops sti_ops = { diff --git a/drivers/gpu/drm/sti/sti_drv.h b/drivers/gpu/drm/sti/sti_drv.h index 30ddc20841c3..78ebe5e30f53 100644 --- a/drivers/gpu/drm/sti/sti_drv.h +++ b/drivers/gpu/drm/sti/sti_drv.h @@ -24,6 +24,7 @@ struct sti_private { struct sti_compositor *compo; struct drm_property *plane_zorder_property; struct drm_device *drm_dev; + struct drm_fbdev_cma *fbdev; struct { struct drm_atomic_state *state; diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index e2901667eceb..ec3108074350 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -404,24 +404,29 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force) return connector_status_disconnected; } -static void sti_dvo_connector_destroy(struct drm_connector *connector) +static int sti_dvo_late_register(struct drm_connector *connector) { struct sti_dvo_connector *dvo_connector = to_sti_dvo_connector(connector); + struct sti_dvo *dvo = dvo_connector->dvo; + + if (dvo_debugfs_init(dvo, dvo->drm_dev->primary)) { + DRM_ERROR("DVO debugfs setup failed\n"); + return -EINVAL; + } - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(dvo_connector); + return 0; } static const struct drm_connector_funcs sti_dvo_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = sti_dvo_connector_detect, - .destroy = sti_dvo_connector_destroy, + .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = sti_dvo_late_register, }; static struct drm_encoder *sti_dvo_find_encoder(struct drm_device *dev) @@ -492,26 +497,16 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) drm_connector_helper_add(drm_connector, &sti_dvo_connector_helper_funcs); - err = drm_connector_register(drm_connector); - if (err) - goto err_connector; - err = drm_mode_connector_attach_encoder(drm_connector, encoder); if (err) { DRM_ERROR("Failed to attach a connector to a encoder\n"); goto err_sysfs; } - if (dvo_debugfs_init(dvo, drm_dev->primary)) - DRM_ERROR("DVO debugfs setup failed\n"); - return 0; err_sysfs: - drm_connector_unregister(drm_connector); -err_connector: drm_bridge_remove(bridge); - drm_connector_cleanup(drm_connector); return -EINVAL; } diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index fdf69b5a041b..bf63086a3dc8 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -866,6 +866,33 @@ static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { .atomic_disable = sti_gdp_atomic_disable, }; +static void sti_gdp_destroy(struct drm_plane *drm_plane) +{ + DRM_DEBUG_DRIVER("\n"); + + drm_plane_helper_disable(drm_plane); + drm_plane_cleanup(drm_plane); +} + +static int sti_gdp_late_register(struct drm_plane *drm_plane) +{ + struct sti_plane *plane = to_sti_plane(drm_plane); + struct sti_gdp *gdp = to_sti_gdp(plane); + + return gdp_debugfs_init(gdp, drm_plane->dev->primary); +} + +struct drm_plane_funcs sti_gdp_plane_helpers_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = sti_gdp_destroy, + .set_property = sti_plane_set_property, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .late_register = sti_gdp_late_register, +}; + struct drm_plane *sti_gdp_create(struct drm_device *drm_dev, struct device *dev, int desc, void __iomem *baseaddr, @@ -892,7 +919,7 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev, res = drm_universal_plane_init(drm_dev, &gdp->plane.drm_plane, possible_crtcs, - &sti_plane_helpers_funcs, + &sti_gdp_plane_helpers_funcs, gdp_supported_formats, ARRAY_SIZE(gdp_supported_formats), type, NULL); @@ -905,9 +932,6 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev, sti_plane_init_property(&gdp->plane, type); - if (gdp_debugfs_init(gdp, drm_dev->primary)) - DRM_ERROR("GDP debugfs setup failed\n"); - return &gdp->plane.drm_plane; err: diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index dcec5a8eda59..8505569f75de 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -681,24 +681,29 @@ sti_hda_connector_detect(struct drm_connector *connector, bool force) return connector_status_connected; } -static void sti_hda_connector_destroy(struct drm_connector *connector) +static int sti_hda_late_register(struct drm_connector *connector) { struct sti_hda_connector *hda_connector = to_sti_hda_connector(connector); + struct sti_hda *hda = hda_connector->hda; + + if (hda_debugfs_init(hda, hda->drm_dev->primary)) { + DRM_ERROR("HDA debugfs setup failed\n"); + return -EINVAL; + } - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(hda_connector); + return 0; } static const struct drm_connector_funcs sti_hda_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = sti_hda_connector_detect, - .destroy = sti_hda_connector_destroy, + .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = sti_hda_late_register, }; static struct drm_encoder *sti_hda_find_encoder(struct drm_device *dev) @@ -756,10 +761,6 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) drm_connector_helper_add(drm_connector, &sti_hda_connector_helper_funcs); - err = drm_connector_register(drm_connector); - if (err) - goto err_connector; - err = drm_mode_connector_attach_encoder(drm_connector, encoder); if (err) { DRM_ERROR("Failed to attach a connector to a encoder\n"); @@ -769,15 +770,10 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) /* force to disable hd dacs at startup */ hda_enable_hd_dacs(hda, false); - if (hda_debugfs_init(hda, drm_dev->primary)) - DRM_ERROR("HDA debugfs setup failed\n"); - return 0; err_sysfs: - drm_connector_unregister(drm_connector); -err_connector: - drm_connector_cleanup(drm_connector); + drm_bridge_remove(bridge); return -EINVAL; } diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 36d9d6635784..8d1402b245bf 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -915,16 +915,6 @@ sti_hdmi_connector_detect(struct drm_connector *connector, bool force) return connector_status_disconnected; } -static void sti_hdmi_connector_destroy(struct drm_connector *connector) -{ - struct sti_hdmi_connector *hdmi_connector - = to_sti_hdmi_connector(connector); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(hdmi_connector); -} - static void sti_hdmi_connector_init_property(struct drm_device *drm_dev, struct drm_connector *connector) { @@ -1007,17 +997,31 @@ sti_hdmi_connector_get_property(struct drm_connector *connector, return -EINVAL; } +static int sti_hdmi_late_register(struct drm_connector *connector) +{ + struct sti_hdmi_connector *hdmi_connector + = to_sti_hdmi_connector(connector); + struct sti_hdmi *hdmi = hdmi_connector->hdmi; + + if (hdmi_debugfs_init(hdmi, hdmi->drm_dev->primary)) { + DRM_ERROR("HDMI debugfs setup failed\n"); + return -EINVAL; + } + + return 0; +} + static const struct drm_connector_funcs sti_hdmi_connector_funcs = { - .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = sti_hdmi_connector_detect, - .destroy = sti_hdmi_connector_destroy, + .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .set_property = drm_atomic_helper_connector_set_property, .atomic_set_property = sti_hdmi_connector_set_property, .atomic_get_property = sti_hdmi_connector_get_property, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = sti_hdmi_late_register, }; static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev) @@ -1078,10 +1082,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) /* initialise property */ sti_hdmi_connector_init_property(drm_dev, drm_connector); - err = drm_connector_register(drm_connector); - if (err) - goto err_connector; - err = drm_mode_connector_attach_encoder(drm_connector, encoder); if (err) { DRM_ERROR("Failed to attach a connector to a encoder\n"); @@ -1091,16 +1091,10 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) /* Enable default interrupts */ hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); - if (hdmi_debugfs_init(hdmi, drm_dev->primary)) - DRM_ERROR("HDMI debugfs setup failed\n"); - return 0; err_sysfs: - drm_connector_unregister(drm_connector); -err_connector: - drm_connector_cleanup(drm_connector); - + drm_bridge_remove(bridge); return -EINVAL; } diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index 1c06a50fddca..33d2f42550cc 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -1234,6 +1234,33 @@ static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = { .atomic_disable = sti_hqvdp_atomic_disable, }; +static void sti_hqvdp_destroy(struct drm_plane *drm_plane) +{ + DRM_DEBUG_DRIVER("\n"); + + drm_plane_helper_disable(drm_plane); + drm_plane_cleanup(drm_plane); +} + +static int sti_hqvdp_late_register(struct drm_plane *drm_plane) +{ + struct sti_plane *plane = to_sti_plane(drm_plane); + struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); + + return hqvdp_debugfs_init(hqvdp, drm_plane->dev->primary); +} + +struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = sti_hqvdp_destroy, + .set_property = sti_plane_set_property, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .late_register = sti_hqvdp_late_register, +}; + static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev, struct device *dev, int desc) { @@ -1246,7 +1273,7 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev, sti_hqvdp_init(hqvdp); res = drm_universal_plane_init(drm_dev, &hqvdp->plane.drm_plane, 1, - &sti_plane_helpers_funcs, + &sti_hqvdp_plane_helpers_funcs, hqvdp_supported_formats, ARRAY_SIZE(hqvdp_supported_formats), DRM_PLANE_TYPE_OVERLAY, NULL); @@ -1259,9 +1286,6 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev, sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY); - if (hqvdp_debugfs_init(hqvdp, drm_dev->primary)) - DRM_ERROR("HQVDP debugfs setup failed\n"); - return &hqvdp->plane.drm_plane; } diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c index 6f86f2b2b6a5..1885c7ab5a8b 100644 --- a/drivers/gpu/drm/sti/sti_mixer.c +++ b/drivers/gpu/drm/sti/sti_mixer.c @@ -181,7 +181,7 @@ static struct drm_info_list mixer1_debugfs_files[] = { { "mixer_aux", mixer_dbg_show, 0, NULL }, }; -static int mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor) +int sti_mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor) { unsigned int i; struct drm_info_list *mixer_debugfs_files; @@ -393,8 +393,5 @@ struct sti_mixer *sti_mixer_create(struct device *dev, DRM_DEBUG_DRIVER("%s created. Regs=%p\n", sti_mixer_to_str(mixer), mixer->regs); - if (mixer_debugfs_init(mixer, drm_dev->primary)) - DRM_ERROR("MIXER debugfs setup failed\n"); - return mixer; } diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h index 6f35fc086873..830a3c42d886 100644 --- a/drivers/gpu/drm/sti/sti_mixer.h +++ b/drivers/gpu/drm/sti/sti_mixer.h @@ -55,6 +55,8 @@ int sti_mixer_active_video_area(struct sti_mixer *mixer, void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable); +int sti_mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor); + /* depth in Cross-bar control = z order */ #define GAM_MIXER_NB_DEPTH_LEVEL 6 diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c index f10c98d3f012..85cee9098439 100644 --- a/drivers/gpu/drm/sti/sti_plane.c +++ b/drivers/gpu/drm/sti/sti_plane.c @@ -106,17 +106,9 @@ void sti_plane_update_fps(struct sti_plane *plane, plane->fps_info.fips_str); } -static void sti_plane_destroy(struct drm_plane *drm_plane) -{ - DRM_DEBUG_DRIVER("\n"); - - drm_plane_helper_disable(drm_plane); - drm_plane_cleanup(drm_plane); -} - -static int sti_plane_set_property(struct drm_plane *drm_plane, - struct drm_property *property, - uint64_t val) +int sti_plane_set_property(struct drm_plane *drm_plane, + struct drm_property *property, + uint64_t val) { struct drm_device *dev = drm_plane->dev; struct sti_private *private = dev->dev_private; @@ -170,13 +162,3 @@ void sti_plane_init_property(struct sti_plane *plane, plane->drm_plane.base.id, sti_plane_to_str(plane), plane->zorder); } - -struct drm_plane_funcs sti_plane_helpers_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, - .destroy = sti_plane_destroy, - .set_property = sti_plane_set_property, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, -}; diff --git a/drivers/gpu/drm/sti/sti_plane.h b/drivers/gpu/drm/sti/sti_plane.h index c50a3b9f5d37..39d39f5b7dd9 100644 --- a/drivers/gpu/drm/sti/sti_plane.h +++ b/drivers/gpu/drm/sti/sti_plane.h @@ -11,8 +11,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_plane_helper.h> -extern struct drm_plane_funcs sti_plane_helpers_funcs; - #define to_sti_plane(x) container_of(x, struct sti_plane, drm_plane) #define STI_PLANE_TYPE_SHIFT 8 @@ -83,6 +81,11 @@ const char *sti_plane_to_str(struct sti_plane *plane); void sti_plane_update_fps(struct sti_plane *plane, bool new_frame, bool new_field); + +int sti_plane_set_property(struct drm_plane *drm_plane, + struct drm_property *property, + uint64_t val); + void sti_plane_init_property(struct sti_plane *plane, enum drm_plane_type type); #endif diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index 60fe0afa5644..e25995b35715 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -112,6 +112,7 @@ struct sti_tvout { struct drm_encoder *hdmi; struct drm_encoder *hda; struct drm_encoder *dvo; + bool debugfs_registered; }; struct sti_tvout_encoder { @@ -625,8 +626,37 @@ static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) kfree(sti_encoder); } +static int sti_tvout_late_register(struct drm_encoder *encoder) +{ + struct sti_tvout *tvout = to_sti_tvout(encoder); + int ret; + + if (tvout->debugfs_registered) + return 0; + + ret = tvout_debugfs_init(tvout, encoder->dev->primary); + if (ret) + return ret; + + tvout->debugfs_registered = true; + return 0; +} + +static void sti_tvout_early_unregister(struct drm_encoder *encoder) +{ + struct sti_tvout *tvout = to_sti_tvout(encoder); + + if (!tvout->debugfs_registered) + return; + + tvout_debugfs_exit(tvout, encoder->dev->primary); + tvout->debugfs_registered = false; +} + static const struct drm_encoder_funcs sti_tvout_encoder_funcs = { .destroy = sti_tvout_encoder_destroy, + .late_register = sti_tvout_late_register, + .early_unregister = sti_tvout_early_unregister, }; static void sti_dvo_encoder_enable(struct drm_encoder *encoder) @@ -813,9 +843,6 @@ static int sti_tvout_bind(struct device *dev, struct device *master, void *data) sti_tvout_create_encoders(drm_dev, tvout); - if (tvout_debugfs_init(tvout, drm_dev->primary)) - DRM_ERROR("TVOUT debugfs setup failed\n"); - return 0; } @@ -823,11 +850,8 @@ static void sti_tvout_unbind(struct device *dev, struct device *master, void *data) { struct sti_tvout *tvout = dev_get_drvdata(dev); - struct drm_device *drm_dev = data; sti_tvout_destroy_encoders(tvout); - - tvout_debugfs_exit(tvout, drm_dev->primary); } static const struct component_ops sti_tvout_ops = { diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c index 0132aaebe598..47634a0251fc 100644 --- a/drivers/gpu/drm/sti/sti_vid.c +++ b/drivers/gpu/drm/sti/sti_vid.c @@ -123,7 +123,7 @@ static struct drm_info_list vid_debugfs_files[] = { { "vid", vid_dbg_show, 0, NULL }, }; -static int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor) +int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor) { unsigned int i; @@ -220,8 +220,5 @@ struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev, sti_vid_init(vid); - if (vid_debugfs_init(vid, drm_dev->primary)) - DRM_ERROR("VID debugfs setup failed\n"); - return vid; } diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h index 6c842344f3d8..fdc90f922a05 100644 --- a/drivers/gpu/drm/sti/sti_vid.h +++ b/drivers/gpu/drm/sti/sti_vid.h @@ -26,4 +26,6 @@ void sti_vid_disable(struct sti_vid *vid); struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev, int id, void __iomem *baseaddr); +int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor); + #endif diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 68e9d85085fb..9a67f927a53e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -24,34 +24,6 @@ #include "sun4i_layer.h" #include "sun4i_tcon.h" -static int sun4i_drv_connector_plug_all(struct drm_device *drm) -{ - struct drm_connector *connector, *failed; - int ret; - - mutex_lock(&drm->mode_config.mutex); - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - ret = drm_connector_register(connector); - if (ret) { - failed = connector; - goto err; - } - } - mutex_unlock(&drm->mode_config.mutex); - return 0; - -err: - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - if (failed == connector) - break; - - drm_connector_unregister(connector); - } - mutex_unlock(&drm->mode_config.mutex); - - return ret; -} - static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe) { struct sun4i_drv *drv = drm->dev_private; @@ -135,10 +107,6 @@ static int sun4i_drv_bind(struct device *dev) if (!drm) return -ENOMEM; - ret = drm_dev_set_unique(drm, dev_name(drm->dev)); - if (ret) - goto free_drm; - drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); if (!drv) { ret = -ENOMEM; @@ -187,14 +155,8 @@ static int sun4i_drv_bind(struct device *dev) if (ret) goto free_drm; - ret = sun4i_drv_connector_plug_all(drm); - if (ret) - goto unregister_drm; - return 0; -unregister_drm: - drm_dev_unregister(drm); free_drm: drm_dev_unref(drm); return ret; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 308e197908fc..d27809372d54 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -541,7 +541,6 @@ static struct drm_driver tilcdc_driver = { .load = tilcdc_load, .unload = tilcdc_unload, .lastclose = tilcdc_lastclose, - .set_busid = drm_platform_set_busid, .irq_handler = tilcdc_irq, .irq_preinstall = tilcdc_irq_preinstall, .irq_postinstall = tilcdc_irq_postinstall, diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 58b8fc036332..9e88231b8906 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -176,7 +176,6 @@ static int vc4_drm_bind(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm; - struct drm_connector *connector; struct vc4_dev *vc4; int ret = 0; @@ -211,22 +210,10 @@ static int vc4_drm_bind(struct device *dev) if (ret < 0) goto unbind_all; - /* Connector registration has to occur after DRM device - * registration, because it creates sysfs entries based on the - * DRM device. - */ - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - ret = drm_connector_register(connector); - if (ret) - goto unregister; - } - vc4_kms_load(drm); return 0; -unregister: - drm_dev_unregister(drm); unbind_all: component_unbind_all(dev, drm); gem_destroy: diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 1b4cc8b27080..35ea5d02a827 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -260,8 +260,6 @@ static int __init vgem_init(void) goto out; } - drm_dev_set_unique(vgem_device, "vgem"); - ret = drm_dev_register(vgem_device, 0); if (ret) diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c index 4f20742e7788..a04ef1c992d9 100644 --- a/drivers/gpu/drm/via/via_mm.c +++ b/drivers/gpu/drm/via/via_mm.c @@ -208,7 +208,7 @@ void via_reclaim_buffers_locked(struct drm_device *dev, struct via_file_private *file_priv = file->driver_priv; struct via_memblock *entry, *next; - if (!(file->minor->master && file->master->lock.hw_lock)) + if (!(dev->master && file->master->lock.hw_lock)) return; drm_legacy_idlelock_take(&file->master->lock); diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 88a39165edd5..7f0e93f87a55 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -27,16 +27,6 @@ #include "virtgpu_drv.h" -int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master) -{ - struct pci_dev *pdev = dev->pdev; - - if (pdev) { - return drm_pci_set_busid(dev, master); - } - return 0; -} - static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev) { struct apertures_struct *ap; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 5820b7020ae5..c13f70cfc461 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -117,7 +117,6 @@ static const struct file_operations virtio_gpu_driver_fops = { static struct drm_driver driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER | DRIVER_ATOMIC, - .set_busid = drm_virtio_set_busid, .load = virtio_gpu_driver_load, .unload = virtio_gpu_driver_unload, .open = virtio_gpu_driver_open, diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index acf556a35cb2..b18ef3111f0c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -49,7 +49,6 @@ #define DRIVER_PATCHLEVEL 1 /* virtgpu_drm_bus.c */ -int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master); int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev); struct virtio_gpu_object { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 9fcd8200d485..60646644bef3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1049,7 +1049,7 @@ static struct vmw_master *vmw_master_check(struct drm_device *dev, if (unlikely(ret != 0)) return ERR_PTR(-ERESTARTSYS); - if (file_priv->is_master) { + if (drm_is_current_master(file_priv)) { mutex_unlock(&dev->master_mutex); return NULL; } @@ -1228,8 +1228,7 @@ static int vmw_master_set(struct drm_device *dev, } static void vmw_master_drop(struct drm_device *dev, - struct drm_file *file_priv, - bool from_release) + struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 1980e2a28265..9a90f824814e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -32,6 +32,7 @@ #include <drm/drmP.h> #include <drm/vmwgfx_drm.h> #include <drm/drm_hashtab.h> +#include <drm/drm_auth.h> #include <linux/suspend.h> #include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_object.h> |