diff options
Diffstat (limited to 'drivers/gpu/drm/arm/malidp_drv.c')
| -rw-r--r-- | drivers/gpu/drm/arm/malidp_drv.c | 577 |
1 files changed, 389 insertions, 188 deletions
diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 01b13d219917..b765f6c9eea4 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -1,38 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * (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/console.h> #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/debugfs.h> -#include <drm/drmP.h> +#include <drm/clients/drm_client_setup.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_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_dma.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_module.h> #include <drm/drm_of.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> #include "malidp_drv.h" +#include "malidp_mw.h" #include "malidp_regs.h" #include "malidp_hw.h" #define MALIDP_CONF_VALID_TIMEOUT 250 +#define AFBC_HEADER_SIZE 16 +#define AFBC_SUPERBLK_ALIGNMENT 128 static void malidp_write_gamma_table(struct malidp_hw_device *hwdev, u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) @@ -46,10 +54,10 @@ static void malidp_write_gamma_table(struct malidp_hw_device *hwdev, * directly. */ malidp_hw_write(hwdev, gamma_write_mask, - hwdev->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); + hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) malidp_hw_write(hwdev, data[i], - hwdev->map.coeffs_base + + hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_DATA); } @@ -102,7 +110,7 @@ void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc, for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i) malidp_hw_write(hwdev, mc->coloradj_coeffs[i], - hwdev->map.coeffs_base + + hwdev->hw->map.coeffs_base + MALIDP_COLOR_ADJ_COEF + 4 * i); malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ, @@ -119,8 +127,8 @@ static void malidp_atomic_commit_se_config(struct drm_crtc *crtc, struct malidp_hw_device *hwdev = malidp->dev; struct malidp_se_config *s = &cs->scaler_config; struct malidp_se_config *old_s = &old_cs->scaler_config; - u32 se_control = hwdev->map.se_base + - ((hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? + u32 se_control = hwdev->hw->map.se_base + + ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? 0x10 : 0xC); u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL; u32 scr = se_control + MALIDP_SE_SCALING_CONTROL; @@ -134,7 +142,7 @@ static void malidp_atomic_commit_se_config(struct drm_crtc *crtc, return; } - hwdev->se_set_scaling_coeffs(hwdev, s, old_s); + hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s); val = malidp_hw_read(hwdev, se_control); val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN; @@ -164,51 +172,60 @@ static void malidp_atomic_commit_se_config(struct drm_crtc *crtc, */ static int malidp_set_and_wait_config_valid(struct drm_device *drm) { - struct malidp_drm *malidp = drm->dev_private; + struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; int ret; - atomic_set(&malidp->config_valid, 0); - hwdev->set_config_valid(hwdev); + hwdev->hw->set_config_valid(hwdev, 1); /* don't wait for config_valid flag if we are in config mode */ - if (hwdev->in_config_mode(hwdev)) + if (hwdev->hw->in_config_mode(hwdev)) { + atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); return 0; + } ret = wait_event_interruptible_timeout(malidp->wq, - atomic_read(&malidp->config_valid) == 1, + atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE, 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; + struct malidp_drm *malidp = drm_to_malidp(drm); + int loop = 5; + + malidp->event = malidp->crtc.state->event; + malidp->crtc.state->event = NULL; + + if (malidp->crtc.state->active) { + /* + * if we have an event to deliver to userspace, make sure + * the vblank is enabled as we are sending it from the IRQ + * handler. + */ + if (malidp->event) + drm_crtc_vblank_get(&malidp->crtc); - if (malidp->crtc.enabled) { /* only set config_valid if the CRTC is enabled */ - if (malidp_set_and_wait_config_valid(drm)) + if (malidp_set_and_wait_config_valid(drm) < 0) { + /* + * make a loop around the second CVAL setting and + * try 5 times before giving up. + */ + while (loop--) { + if (!malidp_set_and_wait_config_valid(drm)) + break; + } DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); - } - - event = malidp->crtc.state->event; - if (event) { - malidp->crtc.state->event = NULL; + } + } else if (malidp->event) { + /* CRTC inactive means vblank IRQ is disabled, send event directly */ 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); + drm_crtc_send_vblank_event(&malidp->crtc, malidp->event); + malidp->event = NULL; spin_unlock_irq(&drm->event_lock); } drm_atomic_helper_commit_hw_done(state); @@ -217,27 +234,38 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) static void malidp_atomic_commit_tail(struct drm_atomic_state *state) { struct drm_device *drm = state->dev; + struct malidp_drm *malidp = drm_to_malidp(drm); struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int i; + bool fence_cookie = dma_fence_begin_signalling(); pm_runtime_get_sync(drm->dev); + /* + * set config_valid to a special value to let IRQ handlers + * know that we are updating registers + */ + atomic_set(&malidp->config_valid, MALIDP_CONFIG_START); + malidp->dev->hw->set_config_valid(malidp->dev, 0); + drm_atomic_helper_commit_modeset_disables(drm, state); - for_each_crtc_in_state(state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { malidp_atomic_commit_update_gamma(crtc, old_crtc_state); malidp_atomic_commit_update_coloradj(crtc, old_crtc_state); malidp_atomic_commit_se_config(crtc, old_crtc_state); } - drm_atomic_helper_commit_planes(drm, state, 0); + drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); + + malidp_mw_atomic_commit(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); malidp_atomic_commit_hw_done(state); - drm_atomic_helper_wait_for_vblanks(drm, state); + dma_fence_end_signalling(fence_cookie); pm_runtime_put(drm->dev); @@ -248,9 +276,119 @@ static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { .atomic_commit_tail = malidp_atomic_commit_tail, }; +static bool +malidp_verify_afbc_framebuffer_caps(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + if (malidp_format_mod_supported(dev, mode_cmd->pixel_format, + mode_cmd->modifier[0]) == false) + return false; + + if (mode_cmd->offsets[0] != 0) { + DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n"); + return false; + } + + switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { + case AFBC_SIZE_16X16: + if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) { + DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n"); + return false; + } + break; + default: + DRM_DEBUG_KMS("Unsupported AFBC block size\n"); + return false; + } + + return true; +} + +static bool +malidp_verify_afbc_framebuffer_size(struct drm_device *dev, + struct drm_file *file, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int n_superblocks = 0; + struct drm_gem_object *objs = NULL; + u32 afbc_superblock_size = 0, afbc_superblock_height = 0; + u32 afbc_superblock_width = 0, afbc_size = 0; + int bpp = 0; + + switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { + case AFBC_SIZE_16X16: + afbc_superblock_height = 16; + afbc_superblock_width = 16; + break; + default: + DRM_DEBUG_KMS("AFBC superblock size is not supported\n"); + return false; + } + + n_superblocks = (mode_cmd->width / afbc_superblock_width) * + (mode_cmd->height / afbc_superblock_height); + + bpp = malidp_format_get_bpp(info->format); + + afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height) + / BITS_PER_BYTE; + + afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT); + afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT); + + if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) { + DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) " + "should be same as width (=%u) * bpp (=%u)\n", + (mode_cmd->pitches[0] * BITS_PER_BYTE), + mode_cmd->width, bpp); + return false; + } + + objs = drm_gem_object_lookup(file, mode_cmd->handles[0]); + if (!objs) { + DRM_DEBUG_KMS("Failed to lookup GEM object\n"); + return false; + } + + if (objs->size < afbc_size) { + DRM_DEBUG_KMS("buffer size (%zu) too small for AFBC buffer size = %u\n", + objs->size, afbc_size); + drm_gem_object_put(objs); + return false; + } + + drm_gem_object_put(objs); + + return true; +} + +static bool +malidp_verify_afbc_framebuffer(struct drm_device *dev, struct drm_file *file, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + if (malidp_verify_afbc_framebuffer_caps(dev, mode_cmd)) + return malidp_verify_afbc_framebuffer_size(dev, file, info, mode_cmd); + + return false; +} + +static struct drm_framebuffer * +malidp_fb_create(struct drm_device *dev, struct drm_file *file, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + if (mode_cmd->modifier[0]) { + if (!malidp_verify_afbc_framebuffer(dev, file, info, mode_cmd)) + return ERR_PTR(-EINVAL); + } + + return drm_gem_fb_create(dev, file, info, mode_cmd); +} + static const struct drm_mode_config_funcs malidp_mode_config_funcs = { - .fb_create = drm_fb_cma_create, - .output_poll_changed = malidp_output_poll_changed, + .fb_create = malidp_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -258,10 +396,12 @@ static const struct drm_mode_config_funcs malidp_mode_config_funcs = { static int malidp_init(struct drm_device *drm) { int ret; - struct malidp_drm *malidp = drm->dev_private; + struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; - drm_mode_config_init(drm); + ret = drmm_mode_config_init(drm); + if (ret) + goto out; drm->mode_config.min_width = hwdev->min_line_size; drm->mode_config.min_height = hwdev->min_line_size; @@ -271,24 +411,23 @@ static int malidp_init(struct drm_device *drm) drm->mode_config.helper_private = &malidp_mode_config_helpers; ret = malidp_crtc_init(drm); - if (ret) { - drm_mode_config_cleanup(drm); - return ret; - } + if (ret) + goto out; - return 0; -} + ret = malidp_mw_connector_init(drm); + if (ret) + goto out; -static void malidp_fini(struct drm_device *drm) -{ - malidp_de_planes_destroy(drm); - drm_mode_config_cleanup(drm); +out: + return ret; } 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); + struct malidp_drm *malidp = drm_to_malidp(drm); + struct malidp_hw_device *hwdev = malidp->dev; /* fetch the interrupts from DT */ irq_de = platform_get_irq_byname(pdev, "DE"); @@ -308,44 +447,130 @@ static int malidp_irq_init(struct platform_device *pdev) ret = malidp_se_irq_init(drm, irq_se); if (ret) { - malidp_de_irq_fini(drm); + malidp_de_irq_fini(hwdev); return ret; } return 0; } -static void malidp_lastclose(struct drm_device *drm) +DEFINE_DRM_GEM_DMA_FOPS(fops); + +static int malidp_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + struct malidp_drm *malidp = drm_to_malidp(drm); + /* allocate for the worst case scenario, i.e. rotated buffers */ + u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 1); + + args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); + + return drm_gem_dma_dumb_create_internal(file_priv, drm, args); +} + +#ifdef CONFIG_DEBUG_FS + +static void malidp_error_stats_init(struct malidp_error_stats *error_stats) +{ + error_stats->num_errors = 0; + error_stats->last_error_status = 0; + error_stats->last_error_vblank = -1; +} + +void malidp_error(struct malidp_drm *malidp, + struct malidp_error_stats *error_stats, u32 status, + u64 vblank) +{ + unsigned long irqflags; + + spin_lock_irqsave(&malidp->errors_lock, irqflags); + error_stats->last_error_status = status; + error_stats->last_error_vblank = vblank; + error_stats->num_errors++; + spin_unlock_irqrestore(&malidp->errors_lock, irqflags); +} + +static void malidp_error_stats_dump(const char *prefix, + struct malidp_error_stats error_stats, + struct seq_file *m) +{ + seq_printf(m, "[%s] num_errors : %d\n", prefix, + error_stats.num_errors); + seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix, + error_stats.last_error_status); + seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix, + error_stats.last_error_vblank); +} + +static int malidp_show_stats(struct seq_file *m, void *arg) +{ + struct drm_device *drm = m->private; + struct malidp_drm *malidp = drm_to_malidp(drm); + unsigned long irqflags; + struct malidp_error_stats de_errors, se_errors; + + spin_lock_irqsave(&malidp->errors_lock, irqflags); + de_errors = malidp->de_errors; + se_errors = malidp->se_errors; + spin_unlock_irqrestore(&malidp->errors_lock, irqflags); + malidp_error_stats_dump("DE", de_errors, m); + malidp_error_stats_dump("SE", se_errors, m); + return 0; +} + +static int malidp_debugfs_open(struct inode *inode, struct file *file) { - struct malidp_drm *malidp = drm->dev_private; + return single_open(file, malidp_show_stats, inode->i_private); +} - drm_fbdev_cma_restore_mode(malidp->fbdev); +static ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_device *drm = m->private; + struct malidp_drm *malidp = drm_to_malidp(drm); + unsigned long irqflags; + + spin_lock_irqsave(&malidp->errors_lock, irqflags); + malidp_error_stats_init(&malidp->de_errors); + malidp_error_stats_init(&malidp->se_errors); + spin_unlock_irqrestore(&malidp->errors_lock, irqflags); + return len; } -DEFINE_DRM_GEM_CMA_FOPS(fops); +static const struct file_operations malidp_debugfs_fops = { + .owner = THIS_MODULE, + .open = malidp_debugfs_open, + .read = seq_read, + .write = malidp_debugfs_write, + .llseek = seq_lseek, + .release = single_release, +}; -static struct drm_driver malidp_driver = { - .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | - DRIVER_PRIME, - .lastclose = malidp_lastclose, - .gem_free_object_unlocked = drm_gem_cma_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, - .dumb_create = drm_gem_cma_dumb_create, - .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, +static void malidp_debugfs_init(struct drm_minor *minor) +{ + struct malidp_drm *malidp = drm_to_malidp(minor->dev); + + malidp_error_stats_init(&malidp->de_errors); + malidp_error_stats_init(&malidp->se_errors); + spin_lock_init(&malidp->errors_lock); + debugfs_create_file("debug", S_IRUGO | S_IWUSR, minor->debugfs_root, + minor->dev, &malidp_debugfs_fops); +} + +#endif //CONFIG_DEBUG_FS + +static const struct drm_driver malidp_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(malidp_dumb_create), + DRM_FBDEV_DMA_DRIVER_OPS, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = malidp_debugfs_init, +#endif .fops = &fops, .name = "mali-dp", .desc = "ARM Mali Display Processor driver", - .date = "20160106", .major = 1, .minor = 0, }; @@ -425,39 +650,32 @@ static ssize_t core_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; + struct malidp_drm *malidp = drm_to_malidp(drm); - return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id); + return sysfs_emit(buf, "%08x\n", malidp->core_id); } -DEVICE_ATTR_RO(core_id); - -static int malidp_init_sysfs(struct device *dev) -{ - int ret = device_create_file(dev, &dev_attr_core_id); - - if (ret) - DRM_ERROR("failed to create device file for core_id\n"); - - return ret; -} +static DEVICE_ATTR_RO(core_id); -static void malidp_fini_sysfs(struct device *dev) -{ - device_remove_file(dev, &dev_attr_core_id); -} +static struct attribute *mali_dp_attrs[] = { + &dev_attr_core_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mali_dp); #define MAX_OUTPUT_CHANNELS 3 static int malidp_runtime_pm_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; + struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; /* we can only suspend if the hardware is in config mode */ - WARN_ON(!hwdev->in_config_mode(hwdev)); + WARN_ON(!hwdev->hw->in_config_mode(hwdev)); + malidp_se_irq_fini(hwdev); + malidp_de_irq_fini(hwdev); hwdev->pm_suspended = true; clk_disable_unprepare(hwdev->mclk); clk_disable_unprepare(hwdev->aclk); @@ -469,13 +687,15 @@ static int malidp_runtime_pm_suspend(struct device *dev) static int malidp_runtime_pm_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; + struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; clk_prepare_enable(hwdev->pclk); clk_prepare_enable(hwdev->aclk); clk_prepare_enable(hwdev->mclk); hwdev->pm_suspended = false; + malidp_de_irq_hw_init(hwdev); + malidp_se_irq_hw_init(hwdev); return 0; } @@ -488,28 +708,26 @@ static int malidp_bind(struct device *dev) struct malidp_hw_device *hwdev; struct platform_device *pdev = to_platform_device(dev); struct of_device_id const *dev_id; + struct drm_encoder *encoder; /* 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; + malidp = devm_drm_dev_alloc(dev, &malidp_driver, typeof(*malidp), base); + if (IS_ERR(malidp)) + return PTR_ERR(malidp); - hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL); + drm = &malidp->base; + + hwdev = drmm_kzalloc(drm, 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)); + hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev); malidp->dev = hwdev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hwdev->regs = devm_ioremap_resource(dev, res); + hwdev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hwdev->regs)) return PTR_ERR(hwdev->regs); @@ -534,13 +752,6 @@ static int malidp_bind(struct device *dev) if (ret && ret != -ENODEV) return ret; - drm = drm_dev_alloc(&malidp_driver, dev); - if (IS_ERR(drm)) { - ret = PTR_ERR(drm); - goto alloc_fail; - } - - drm->dev_private = malidp; dev_set_drvdata(dev, drm); /* Enable power management */ @@ -569,18 +780,24 @@ static int malidp_bind(struct device *dev) goto query_hw_fail; } - ret = hwdev->query_hw(hwdev); + ret = hwdev->hw->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); + version = malidp_hw_read(hwdev, hwdev->hw->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); malidp->core_id = version; + ret = of_property_read_u32(dev->of_node, + "arm,malidp-arqos-value", + &hwdev->arqos_value); + if (ret) + hwdev->arqos_value = 0x0; + /* 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", @@ -590,19 +807,16 @@ static int malidp_bind(struct device *dev) 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); + malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); + hwdev->output_color_depth = out_depth; - atomic_set(&malidp->config_valid, 0); + atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT); init_waitqueue_head(&malidp->wq); ret = malidp_init(drm); if (ret < 0) goto query_hw_fail; - ret = malidp_init_sysfs(dev); - if (ret) - goto init_fail; - /* Set the CRTC's port so that the encoder component can find it */ malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); @@ -612,12 +826,19 @@ static int malidp_bind(struct device *dev) goto bind_fail; } + /* We expect to have a maximum of two encoders one for the actual + * display and a virtual one for the writeback connector + */ + WARN_ON(drm->mode_config.num_encoder > 2); + list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) { + encoder->possible_clones = + (1 << drm->mode_config.num_encoder) - 1; + } + ret = malidp_irq_init(pdev); if (ret < 0) goto irq_init_fail; - drm->irq_enabled = true; - ret = drm_vblank_init(drm, drm->mode_config.num_crtc); if (ret < 0) { DRM_ERROR("failed to initialise vblank\n"); @@ -627,53 +848,35 @@ static int malidp_bind(struct device *dev) drm_mode_config_reset(drm); - malidp->fbdev = drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_connector); - - if (IS_ERR(malidp->fbdev)) { - ret = PTR_ERR(malidp->fbdev); - malidp->fbdev = NULL; - goto fbdev_fail; - } - drm_kms_helper_poll_init(drm); ret = drm_dev_register(drm, 0); if (ret) goto register_fail; + drm_client_setup(drm, NULL); + return 0; register_fail: - if (malidp->fbdev) { - drm_fbdev_cma_fini(malidp->fbdev); - malidp->fbdev = NULL; - } drm_kms_helper_poll_fini(drm); -fbdev_fail: pm_runtime_get_sync(dev); vblank_fail: - malidp_se_irq_fini(drm); - malidp_de_irq_fini(drm); - drm->irq_enabled = false; + malidp_se_irq_fini(hwdev); + malidp_de_irq_fini(hwdev); irq_init_fail: + drm_atomic_helper_shutdown(drm); component_unbind_all(dev, drm); bind_fail: of_node_put(malidp->crtc.port); malidp->crtc.port = NULL; -init_fail: - malidp_fini_sysfs(dev); - malidp_fini(drm); query_hw_fail: pm_runtime_put(dev); if (pm_runtime_enabled(dev)) pm_runtime_disable(dev); else malidp_runtime_pm_suspend(dev); - drm->dev_private = NULL; dev_set_drvdata(dev, NULL); - drm_dev_unref(drm); -alloc_fail: of_reserved_mem_device_release(dev); return ret; @@ -682,30 +885,24 @@ alloc_fail: static void malidp_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; + struct malidp_drm *malidp = drm_to_malidp(drm); + struct malidp_hw_device *hwdev = malidp->dev; drm_dev_unregister(drm); - if (malidp->fbdev) { - drm_fbdev_cma_fini(malidp->fbdev); - malidp->fbdev = NULL; - } drm_kms_helper_poll_fini(drm); pm_runtime_get_sync(dev); - malidp_se_irq_fini(drm); - malidp_de_irq_fini(drm); + drm_atomic_helper_shutdown(drm); + malidp_se_irq_fini(hwdev); + malidp_de_irq_fini(hwdev); component_unbind_all(dev, drm); of_node_put(malidp->crtc.port); malidp->crtc.port = NULL; - malidp_fini_sysfs(dev); - malidp_fini(drm); pm_runtime_put(dev); if (pm_runtime_enabled(dev)) pm_runtime_disable(dev); else malidp_runtime_pm_suspend(dev); - drm->dev_private = NULL; dev_set_drvdata(dev, NULL); - drm_dev_unref(drm); of_reserved_mem_device_release(dev); } @@ -741,63 +938,67 @@ static int malidp_platform_probe(struct platform_device *pdev) match); } -static int malidp_platform_remove(struct platform_device *pdev) +static void malidp_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &malidp_master_ops); - return 0; +} + +static void malidp_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); } static int __maybe_unused malidp_pm_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; - - drm_kms_helper_poll_disable(drm); - console_lock(); - drm_fbdev_cma_set_suspend(malidp->fbdev, 1); - console_unlock(); - malidp->pm_state = drm_atomic_helper_suspend(drm); - if (IS_ERR(malidp->pm_state)) { - console_lock(); - drm_fbdev_cma_set_suspend(malidp->fbdev, 0); - console_unlock(); - drm_kms_helper_poll_enable(drm); - return PTR_ERR(malidp->pm_state); - } - return 0; + return drm_mode_config_helper_suspend(drm); } static int __maybe_unused malidp_pm_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; - drm_atomic_helper_resume(drm, malidp->pm_state); - console_lock(); - drm_fbdev_cma_set_suspend(malidp->fbdev, 0); - console_unlock(); - drm_kms_helper_poll_enable(drm); + drm_mode_config_helper_resume(drm); + + return 0; +} +static int __maybe_unused malidp_pm_suspend_late(struct device *dev) +{ + if (!pm_runtime_status_suspended(dev)) { + malidp_runtime_pm_suspend(dev); + pm_runtime_set_suspended(dev); + } + return 0; +} + +static int __maybe_unused malidp_pm_resume_early(struct device *dev) +{ + malidp_runtime_pm_resume(dev); + pm_runtime_set_active(dev); return 0; } static const struct dev_pm_ops malidp_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ + SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \ SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) }; static struct platform_driver malidp_platform_driver = { .probe = malidp_platform_probe, .remove = malidp_platform_remove, + .shutdown = malidp_platform_shutdown, .driver = { .name = "mali-dp", .pm = &malidp_pm_ops, .of_match_table = malidp_drm_of_match, + .dev_groups = mali_dp_groups, }, }; -module_platform_driver(malidp_platform_driver); +drm_module_platform_driver(malidp_platform_driver); MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); MODULE_DESCRIPTION("ARM Mali DP DRM driver"); |
