diff options
Diffstat (limited to 'drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c')
| -rw-r--r-- | drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 319 |
1 files changed, 175 insertions, 144 deletions
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 68c0c297b3a5..289304500ab0 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* Hisilicon Hibmc SoC drm driver * * Based on the bochs drm driver. @@ -8,40 +9,35 @@ * Rongrong Zou <zourongrong@huawei.com> * Rongrong Zou <zourongrong@gmail.com> * Jianhua Li <lijianhua@huawei.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ -#include <linux/console.h> +#include <linux/aperture.h> #include <linux/module.h> +#include <linux/pci.h> +#include <drm/clients/drm_client_setup.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_ttm.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_module.h> +#include <drm/drm_vblank.h> #include "hibmc_drm_drv.h" #include "hibmc_drm_regs.h" -static const struct file_operations hibmc_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .mmap = hibmc_mmap, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, -}; +#include "dp/dp_reg.h" + +DEFINE_DRM_GEM_FOPS(hibmc_fops); + +static const char *g_irqs_names_map[HIBMC_MAX_VECTORS] = { "hibmc-vblank", "hibmc-hpd" }; -static irqreturn_t hibmc_drm_interrupt(int irq, void *arg) +static irqreturn_t hibmc_interrupt(int irq, void *arg) { struct drm_device *dev = (struct drm_device *)arg; - struct hibmc_drm_private *priv = - (struct hibmc_drm_private *)dev->dev_private; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); u32 status; status = readl(priv->mmio + HIBMC_RAW_INTERRUPT); @@ -55,33 +51,51 @@ static irqreturn_t hibmc_drm_interrupt(int irq, void *arg) return IRQ_HANDLED; } -static struct drm_driver hibmc_driver = { - .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_ATOMIC | DRIVER_HAVE_IRQ, +static irqreturn_t hibmc_dp_interrupt(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + u32 status; + + status = readl(priv->mmio + HIBMC_DP_INTSTAT); + if (status) { + priv->dp.irq_status = status; + writel(status, priv->mmio + HIBMC_DP_INTCLR); + return IRQ_WAKE_THREAD; + } + + return IRQ_HANDLED; +} + +static int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + return drm_gem_vram_fill_create_dumb(file, dev, 0, 128, args); +} + +static const struct drm_driver hibmc_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &hibmc_fops, .name = "hibmc", - .date = "20160828", .desc = "hibmc drm driver", .major = 1, .minor = 0, - .gem_free_object_unlocked = hibmc_gem_free_object, + .debugfs_init = drm_vram_mm_debugfs_init, .dumb_create = hibmc_dumb_create, - .dumb_map_offset = hibmc_dumb_mmap_offset, - .irq_handler = hibmc_drm_interrupt, + .dumb_map_offset = drm_gem_ttm_dumb_map_offset, + DRM_FBDEV_TTM_DRIVER_OPS, }; static int __maybe_unused hibmc_pm_suspend(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_get_drvdata(dev); return drm_mode_config_helper_suspend(drm_dev); } static int __maybe_unused hibmc_pm_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_get_drvdata(dev); return drm_mode_config_helper_resume(drm_dev); } @@ -91,56 +105,74 @@ static const struct dev_pm_ops hibmc_pm_ops = { hibmc_pm_resume) }; +static const struct drm_mode_config_funcs hibmc_mode_funcs = { + .mode_valid = drm_vram_helper_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_gem_fb_create, +}; + static int hibmc_kms_init(struct hibmc_drm_private *priv) { + struct drm_device *dev = &priv->dev; + struct drm_encoder *encoder; + u32 clone_mask = 0; int ret; - drm_mode_config_init(priv->dev); - priv->mode_config_initialized = true; + ret = drmm_mode_config_init(dev); + if (ret) + return ret; - priv->dev->mode_config.min_width = 0; - priv->dev->mode_config.min_height = 0; - priv->dev->mode_config.max_width = 1920; - priv->dev->mode_config.max_height = 1440; + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 1920; + dev->mode_config.max_height = 1200; - priv->dev->mode_config.fb_base = priv->fb_base; - priv->dev->mode_config.preferred_depth = 24; - priv->dev->mode_config.prefer_shadow = 0; + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; - priv->dev->mode_config.funcs = (void *)&hibmc_mode_funcs; + dev->mode_config.funcs = (void *)&hibmc_mode_funcs; ret = hibmc_de_init(priv); if (ret) { - DRM_ERROR("failed to init de: %d\n", ret); + drm_err(dev, "failed to init de: %d\n", ret); return ret; } + /* + * If the serdes reg is readable and is not equal to 0, + * DP block exists and initializes it. + */ + ret = readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL); + if (ret) { + ret = hibmc_dp_init(priv); + if (ret) + drm_err(dev, "failed to init dp: %d\n", ret); + } + ret = hibmc_vdac_init(priv); if (ret) { - DRM_ERROR("failed to init vdac: %d\n", ret); + drm_err(dev, "failed to init vdac: %d\n", ret); return ret; } - return 0; -} + drm_for_each_encoder(encoder, dev) + clone_mask |= drm_encoder_mask(encoder); -static void hibmc_kms_fini(struct hibmc_drm_private *priv) -{ - if (priv->mode_config_initialized) { - drm_mode_config_cleanup(priv->dev); - priv->mode_config_initialized = false; - } + drm_for_each_encoder(encoder, dev) + encoder->possible_clones = clone_mask; + + return 0; } /* * It can operate in one of three modes: 0, 1 or Sleep. */ -void hibmc_set_power_mode(struct hibmc_drm_private *priv, - unsigned int power_mode) +void hibmc_set_power_mode(struct hibmc_drm_private *priv, u32 power_mode) { - unsigned int control_value = 0; + u32 control_value = 0; void __iomem *mmio = priv->mmio; - unsigned int input = 1; + u32 input = 1; if (power_mode > HIBMC_PW_MODE_CTL_MODE_SLEEP) return; @@ -158,8 +190,8 @@ void hibmc_set_power_mode(struct hibmc_drm_private *priv, void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) { - unsigned int gate_reg; - unsigned int mode; + u32 gate_reg; + u32 mode; void __iomem *mmio = priv->mmio; /* Get current power mode. */ @@ -184,7 +216,7 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) static void hibmc_hw_config(struct hibmc_drm_private *priv) { - unsigned int reg; + u32 reg; /* On hardware reset, power mode 0 is default. */ hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0); @@ -217,28 +249,18 @@ static void hibmc_hw_config(struct hibmc_drm_private *priv) static int hibmc_hw_map(struct hibmc_drm_private *priv) { - struct drm_device *dev = priv->dev; - struct pci_dev *pdev = dev->pdev; - resource_size_t addr, size, ioaddr, iosize; + struct drm_device *dev = &priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + resource_size_t ioaddr, iosize; ioaddr = pci_resource_start(pdev, 1); iosize = pci_resource_len(pdev, 1); - priv->mmio = devm_ioremap_nocache(dev->dev, ioaddr, iosize); + priv->mmio = devm_ioremap(dev->dev, ioaddr, iosize); if (!priv->mmio) { - DRM_ERROR("Cannot map mmio region\n"); + drm_err(dev, "Cannot map mmio region\n"); return -ENOMEM; } - addr = pci_resource_start(pdev, 0); - size = pci_resource_len(pdev, 0); - priv->fb_map = devm_ioremap(dev->dev, addr, size); - if (!priv->fb_map) { - DRM_ERROR("Cannot map framebuffer\n"); - return -ENOMEM; - } - priv->fb_base = addr; - priv->fb_size = size; - return 0; } @@ -255,45 +277,64 @@ static int hibmc_hw_init(struct hibmc_drm_private *priv) return 0; } -static int hibmc_unload(struct drm_device *dev) +static void hibmc_unload(struct drm_device *dev) { - struct hibmc_drm_private *priv = dev->dev_private; + drm_atomic_helper_shutdown(dev); +} - hibmc_fbdev_fini(priv); +static int hibmc_msi_init(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + int valid_irq_num; + int irq; + int ret; - drm_atomic_helper_shutdown(dev); + ret = pci_alloc_irq_vectors(pdev, HIBMC_MIN_VECTORS, + HIBMC_MAX_VECTORS, PCI_IRQ_MSI); + if (ret < 0) { + drm_err(dev, "enabling MSI failed: %d\n", ret); + return ret; + } - if (dev->irq_enabled) - drm_irq_uninstall(dev); - if (priv->msi_enabled) - pci_disable_msi(dev->pdev); + valid_irq_num = ret; + + for (int i = 0; i < valid_irq_num; i++) { + irq = pci_irq_vector(pdev, i); + + if (i) + /* PCI devices require shared interrupts. */ + ret = devm_request_threaded_irq(&pdev->dev, irq, + hibmc_dp_interrupt, + hibmc_dp_hpd_isr, + IRQF_SHARED, g_irqs_names_map[i], dev); + else + ret = devm_request_irq(&pdev->dev, irq, hibmc_interrupt, + IRQF_SHARED, g_irqs_names_map[i], dev); + if (ret) { + drm_err(dev, "install irq failed: %d\n", ret); + return ret; + } + } - hibmc_kms_fini(priv); - hibmc_mm_fini(priv); - dev->dev_private = NULL; return 0; } static int hibmc_load(struct drm_device *dev) { - struct hibmc_drm_private *priv; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); int ret; - priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - DRM_ERROR("no memory to allocate for hibmc_drm_private\n"); - return -ENOMEM; - } - dev->dev_private = priv; - priv->dev = dev; - ret = hibmc_hw_init(priv); if (ret) - goto err; + return ret; - ret = hibmc_mm_init(priv); - if (ret) - goto err; + ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (ret) { + drm_err(dev, "Error initializing VRAM MM; %d\n", ret); + return ret; + } ret = hibmc_kms_init(priv); if (ret) @@ -301,80 +342,76 @@ static int hibmc_load(struct drm_device *dev) ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) { - DRM_ERROR("failed to initialize vblank: %d\n", ret); + drm_err(dev, "failed to initialize vblank: %d\n", ret); goto err; } - priv->msi_enabled = 0; - ret = pci_enable_msi(dev->pdev); + ret = hibmc_msi_init(dev); if (ret) { - DRM_WARN("enabling MSI failed: %d\n", ret); - } else { - priv->msi_enabled = 1; - ret = drm_irq_install(dev, dev->pdev->irq); - if (ret) - DRM_WARN("install irq failed: %d\n", ret); + drm_err(dev, "hibmc msi init failed, ret:%d\n", ret); + goto err; } /* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); - ret = hibmc_fbdev_init(priv); - if (ret) { - DRM_ERROR("failed to initialize fbdev: %d\n", ret); - goto err; - } - return 0; err: hibmc_unload(dev); - DRM_ERROR("failed to initialize drm driver: %d\n", ret); + drm_err(dev, "failed to initialize drm driver: %d\n", ret); return ret; } static int hibmc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct hibmc_drm_private *priv; struct drm_device *dev; int ret; - dev = drm_dev_alloc(&hibmc_driver, &pdev->dev); - if (IS_ERR(dev)) { + ret = aperture_remove_conflicting_pci_devices(pdev, hibmc_driver.name); + if (ret) + return ret; + + priv = devm_drm_dev_alloc(&pdev->dev, &hibmc_driver, + struct hibmc_drm_private, dev); + if (IS_ERR(priv)) { DRM_ERROR("failed to allocate drm_device\n"); - return PTR_ERR(dev); + return PTR_ERR(priv); } - dev->pdev = pdev; + dev = &priv->dev; pci_set_drvdata(pdev, dev); - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) { - DRM_ERROR("failed to enable pci device: %d\n", ret); - goto err_free; + drm_err(dev, "failed to enable pci device: %d\n", ret); + goto err_return; } + pci_set_master(pdev); + ret = hibmc_load(dev); if (ret) { - DRM_ERROR("failed to load hibmc: %d\n", ret); - goto err_disable; + drm_err(dev, "failed to load hibmc: %d\n", ret); + goto err_return; } ret = drm_dev_register(dev, 0); if (ret) { - DRM_ERROR("failed to register drv for userspace access: %d\n", + drm_err(dev, "failed to register drv for userspace access: %d\n", ret); goto err_unload; } + + drm_client_setup(dev, NULL); + return 0; err_unload: hibmc_unload(dev); -err_disable: - pci_disable_device(pdev); -err_free: - drm_dev_put(dev); - +err_return: return ret; } @@ -384,10 +421,14 @@ static void hibmc_pci_remove(struct pci_dev *pdev) drm_dev_unregister(dev); hibmc_unload(dev); - drm_dev_put(dev); } -static struct pci_device_id hibmc_pci_table[] = { +static void hibmc_pci_shutdown(struct pci_dev *pdev) +{ + hibmc_pci_remove(pdev); +} + +static const struct pci_device_id hibmc_pci_table[] = { { PCI_VDEVICE(HUAWEI, 0x1711) }, {0,} }; @@ -397,21 +438,11 @@ static struct pci_driver hibmc_pci_driver = { .id_table = hibmc_pci_table, .probe = hibmc_pci_probe, .remove = hibmc_pci_remove, + .shutdown = hibmc_pci_shutdown, .driver.pm = &hibmc_pm_ops, }; -static int __init hibmc_init(void) -{ - return pci_register_driver(&hibmc_pci_driver); -} - -static void __exit hibmc_exit(void) -{ - return pci_unregister_driver(&hibmc_pci_driver); -} - -module_init(hibmc_init); -module_exit(hibmc_exit); +drm_module_pci_driver(hibmc_pci_driver); MODULE_DEVICE_TABLE(pci, hibmc_pci_table); MODULE_AUTHOR("RongrongZou <zourongrong@huawei.com>"); |
