* [PATCH v16 01/15] clocksource/drivers/arm_arch_timer: Move enums and defines to header file
From: fu.wei at linaro.org @ 2016-11-16 13:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479304148-2965-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
To support the arm_arch_timer via ACPI we need to share defines and enums
between the driver and the ACPI parser code.
Split out the relevant defines and enums into arm_arch_timer.h, and
change "enum ppi_nr" to "enum arch_timer_ppi_nr" to avoid the potential
name clashes.
Also switch "enum ppi_nr" to "enum arch_timer_ppi_nr" in
arm_arch_timer.c.
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
drivers/clocksource/arm_arch_timer.c | 13 +------------
include/clocksource/arm_arch_timer.h | 12 ++++++++++++
2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..21068be 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -51,8 +51,6 @@
#define CNTV_TVAL 0x38
#define CNTV_CTL 0x3c
-#define ARCH_CP15_TIMER BIT(0)
-#define ARCH_MEM_TIMER BIT(1)
static unsigned arch_timers_present __initdata;
static void __iomem *arch_counter_base;
@@ -65,20 +63,11 @@ struct arch_timer {
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)
static u32 arch_timer_rate;
-
-enum ppi_nr {
- PHYS_SECURE_PPI,
- PHYS_NONSECURE_PPI,
- VIRT_PPI,
- HYP_PPI,
- MAX_TIMER_PPI
-};
-
static int arch_timer_ppi[MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
-static enum ppi_nr arch_timer_uses_ppi = VIRT_PPI;
+static enum arch_timer_ppi_nr arch_timer_uses_ppi = VIRT_PPI;
static bool arch_timer_c3stop;
static bool arch_timer_mem_use_virtual;
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index caedb74..557f869 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -16,9 +16,13 @@
#ifndef __CLKSOURCE_ARM_ARCH_TIMER_H
#define __CLKSOURCE_ARM_ARCH_TIMER_H
+#include <linux/bitops.h>
#include <linux/timecounter.h>
#include <linux/types.h>
+#define ARCH_CP15_TIMER BIT(0)
+#define ARCH_MEM_TIMER BIT(1)
+
#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
@@ -34,6 +38,14 @@ enum arch_timer_reg {
ARCH_TIMER_REG_TVAL,
};
+enum arch_timer_ppi_nr {
+ PHYS_SECURE_PPI,
+ PHYS_NONSECURE_PPI,
+ VIRT_PPI,
+ HYP_PPI,
+ MAX_TIMER_PPI
+};
+
#define ARCH_TIMER_PHYS_ACCESS 0
#define ARCH_TIMER_VIRT_ACCESS 1
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
--
2.7.4
^ permalink raw reply related
* [PATCH v16 00/15] acpi, clocksource: add GTDT driver and GTDT support in arm_arch_timer
From: fu.wei at linaro.org @ 2016-11-16 13:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Fu Wei <fu.wei@linaro.org>
This patchset:
(1)Preparation for adding GTDT support in arm_arch_timer:
1. Move some enums and marcos to header file;
2. Add a new enum for spi type;
3. Improve printk relevant code.
4. rename some enums and defines, and some cleanups.
5. separate out arch_timer_uses_ppi init code and fix a potential bug
6. Refactor arch_timer_detect_rate to keep dt code only in *_of_init
7. Refactor arch_timer_needs_probing, and call it only if acpi disabled.
8. Introduce some new structs and refactor the timer init code
(2)Introduce ACPI GTDT parser: drivers/acpi/arm64/acpi_gtdt.c
Parse all kinds of timer in GTDT table of ACPI:arch timer,
memory-mapped timer and SBSA Generic Watchdog timer.
This driver can help to simplify all the relevant timer drivers,
and separate all the ACPI GTDT knowledge from them.
(3)Simplify ACPI code for arm_arch_timer
(4)Add GTDT support for ARM memory-mapped timer, also refactor
original memory-mapped timer dt support for reusing some common
code.
This patchset has been tested on the following platforms with ACPI enabled:
(1)ARM Foundation v8 model
Changelog:
v16: https://lkml.org/lkml/2016/
Fix patchset problem about static enum ppi_nr of 01/13 in v15.
Refactor arch_timer_detect_rate.
Refactor arch_timer_needs_probing.
v15: https://lkml.org/lkml/2016/11/15/366
Re-order patches
Add arm_arch_timer refactoring patches to prepare for GTDT:
1. rename some enums and defines, and some cleanups
2. separate out arch_timer_uses_ppi init code and fix a potential bug
3. Improve some new structs, refactor the timer init code.
Since the some structs have been changed, GTDT parser for memory-mapped
timer and SBSA Generic Watchdog timer have been update.
v14: https://lkml.org/lkml/2016/9/28/573
Separate memory-mapped timer GTDT support into two patches
1. Refactor the timer init code to prepare for GTDT
2. Add GTDT support for memory-mapped timer
v13: http://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1231717.html
Improve arm_arch_timer code for memory-mapped
timer GTDT support, refactor original memory-mapped timer
dt support for reusing some common code.
v12: https://lkml.org/lkml/2016/9/13/250
Rebase to latest Linux 4.8-rc6
Delete the confusing "skipping" in the error message.
V11: https://lkml.org/lkml/2016/9/6/354
Rebase to latest Linux 4.8-rc5
Delete typedef (suggested by checkpatch.pl)
V10: https://lkml.org/lkml/2016/7/26/215
Drop the "readq" patch.
Rebase to latest Linux 4.7.
V9: https://lkml.org/lkml/2016/7/25/345
Improve pr_err message in acpi gtdt driver.
Update Commit message for 7/9
shorten the irq mapping function name
Improve GTDT driver for memory-mapped timer
v8: https://lkml.org/lkml/2016/7/19/660
Improve "pr_fmt(fmt)" definition: add "ACPI" in front of "GTDT",
and also improve printk message.
Simplify is_timer_block and is_watchdog.
Merge acpi_gtdt_desc_init and gtdt_arch_timer_init into acpi_gtdt_init();
Delete __init in include/linux/acpi.h for GTDT API
Make ARM64 select GTDT.
Delete "#include <linux/module.h>" from acpi_gtdt.c
Simplify GT block parse code.
v7: https://lkml.org/lkml/2016/7/13/769
Move the GTDT driver to drivers/acpi/arm64
Add add the ARM64-specific ACPI Support maintainers in MAINTAINERS
Merge 3 patches of GTDT parser driver.
Fix the for_each_platform_timer bug.
v6: https://lkml.org/lkml/2016/6/29/580
split the GTDT driver to 4 parts: basic, arch_timer, memory-mapped timer,
and SBSA Generic Watchdog timer
Improve driver by suggestions and example code from Daniel Lezcano
v5: https://lkml.org/lkml/2016/5/24/356
Sorting out all patches, simplify the API of GTDT driver:
GTDT driver just fills the data struct for arm_arch_timer driver.
v4: https://lists.linaro.org/pipermail/linaro-acpi/2016-March/006667.html
Delete the kvm relevant patches
Separate two patches for sorting out the code for arm_arch_timer.
Improve irq info export code to allow missing irq info in GTDT table.
v3: https://lkml.org/lkml/2016/2/1/658
Improve GTDT driver code:
(1)improve pr_* by defining pr_fmt(fmt)
(2)simplify gtdt_sbsa_gwdt_init
(3)improve gtdt_arch_timer_data_init, if table is NULL, it will try
to get GTDT table.
Move enum ppi_nr to arm_arch_timer.h, and add enum spi_nr.
Add arm_arch_timer get ppi from DT and GTDT support for kvm.
v2: https://lkml.org/lkml/2015/12/2/10
Rebase to latest kernel version(4.4-rc3).
Fix the bug about the config problem,
use CONFIG_ACPI_GTDT instead of CONFIG_ACPI in arm_arch_timer.c
v1: The first upstreaming version: https://lkml.org/lkml/2015/10/28/553
Fu Wei (15):
clocksource/drivers/arm_arch_timer: Move enums and defines to header
file
clocksource/drivers/arm_arch_timer: Add a new enum for spi type
clocksource/drivers/arm_arch_timer: Improve printk relevant code
clocksource/drivers/arm_arch_timer: rename some enums and defines, and
some cleanups.
clocksource/drivers/arm_arch_timer: fix a bug in arch_timer_register
about arch_timer_uses_ppi
clocksource/drivers/arm_arch_timer: separate out arch_timer_uses_ppi
init code to prepare for GTDT.
clocksource/drivers/arm_arch_timer: Refactor arch_timer_detect_rate to
keep dt code in *_of_init
clocksource/drivers/arm_arch_timer: Refactor arch_timer_needs_probing,
and call it only if acpi disabled.
clocksource/drivers/arm_arch_timer: Introduce some new structs to
prepare for GTDT
clocksource/drivers/arm_arch_timer: Refactor the timer init code to
prepare for GTDT
acpi/arm64: Add GTDT table parse driver
clocksource/drivers/arm_arch_timer: Simplify ACPI support code.
acpi/arm64: Add memory-mapped timer support in GTDT driver
clocksource/drivers/arm_arch_timer: Add GTDT support for memory-mapped
timer
acpi/arm64: Add SBSA Generic Watchdog support in GTDT driver
arch/arm64/Kconfig | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/arm64/Makefile | 1 +
drivers/acpi/arm64/gtdt.c | 411 +++++++++++++++++++++++++++++
drivers/clocksource/arm_arch_timer.c | 484 ++++++++++++++++++++---------------
drivers/watchdog/Kconfig | 1 +
include/clocksource/arm_arch_timer.h | 61 ++++-
include/linux/acpi.h | 8 +
virt/kvm/arm/hyp/timer-sr.c | 6 +-
9 files changed, 759 insertions(+), 217 deletions(-)
create mode 100644 drivers/acpi/arm64/gtdt.c
--
2.7.4
^ permalink raw reply
* [PATCH v7 7/7] MAINTAINERS: Update HISILICON DRM entries
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2a58eea..84a7296 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4123,6 +4123,7 @@ F: drivers/gpu/drm/gma500/
DRM DRIVERS FOR HISILICON
M: Xinliang Liu <z.liuxinliang@hisilicon.com>
+M: Rongrong Zou <zourongrong@gmail.com>
R: Xinwei Kong <kong.kongxinwei@hisilicon.com>
R: Chen Feng <puck.chen@hisilicon.com>
L: dri-devel at lists.freedesktop.org
--
1.9.1
^ permalink raw reply related
* [PATCH v7 6/7] drm/hisilicon/hibmc: Add support for vblank interrupt
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
Add vblank interrupt.
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 56 ++++++++++++++++++++++++-
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 1 +
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index c133644..73ba8b0 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -39,16 +39,45 @@
static int hibmc_enable_vblank(struct drm_device *dev, unsigned int pipe)
{
+ struct hibmc_drm_private *priv =
+ (struct hibmc_drm_private *)dev->dev_private;
+
+ writel(HIBMC_RAW_INTERRUPT_EN_VBLANK(1),
+ priv->mmio + HIBMC_RAW_INTERRUPT_EN);
+
return 0;
}
static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
{
+ struct hibmc_drm_private *priv =
+ (struct hibmc_drm_private *)dev->dev_private;
+
+ writel(HIBMC_RAW_INTERRUPT_EN_VBLANK(0),
+ priv->mmio + HIBMC_RAW_INTERRUPT_EN);
+}
+
+irqreturn_t hibmc_drm_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;
+ u32 status;
+
+ status = readl(priv->mmio + HIBMC_RAW_INTERRUPT);
+
+ if (status & HIBMC_RAW_INTERRUPT_VBLANK(1)) {
+ writel(HIBMC_RAW_INTERRUPT_VBLANK(1),
+ priv->mmio + HIBMC_RAW_INTERRUPT);
+ drm_handle_vblank(dev, 0);
+ }
+
+ return IRQ_HANDLED;
}
static struct drm_driver hibmc_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET |
- DRIVER_ATOMIC,
+ DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
.fops = &hibmc_fops,
.name = "hibmc",
.date = "20160828",
@@ -62,6 +91,7 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
.dumb_create = hibmc_dumb_create,
.dumb_map_offset = hibmc_dumb_mmap_offset,
.dumb_destroy = drm_gem_dumb_destroy,
+ .irq_handler = hibmc_drm_interrupt,
};
static int hibmc_pm_suspend(struct device *dev)
@@ -268,6 +298,13 @@ static int hibmc_unload(struct drm_device *dev)
struct hibmc_drm_private *priv = dev->dev_private;
hibmc_fbdev_fini(priv);
+
+ if (dev->irq_enabled)
+ drm_irq_uninstall(dev);
+ if (priv->msi_enabled)
+ pci_disable_msi(dev->pdev);
+ drm_vblank_cleanup(dev);
+
hibmc_kms_fini(priv);
hibmc_mm_fini(priv);
dev->dev_private = NULL;
@@ -299,6 +336,23 @@ static int hibmc_load(struct drm_device *dev)
if (ret)
goto err;
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (ret) {
+ DRM_ERROR("failed to initialize vblank: %d\n", ret);
+ goto err;
+ }
+
+ priv->msi_enabled = 0;
+ ret = pci_enable_msi(dev->pdev);
+ 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);
+ }
+
/* reset all the states of crtc/plane/encoder/connector */
drm_mode_config_reset(dev);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index b626caf..e195521 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -42,6 +42,7 @@ struct hibmc_drm_private {
void __iomem *fb_map;
unsigned long fb_base;
unsigned long fb_size;
+ bool msi_enabled;
/* drm */
struct drm_device *dev;
--
1.9.1
^ permalink raw reply related
* [PATCH v7 5/7] drm/hisilicon/hibmc: Add support for VDAC
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
VDAC(Video Digital-to-Analog converter) converts the RGB diaital data
stream from DE to VGA analog signals.
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +-
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 6 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 1 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 147 +++++++++++++++++++++++
4 files changed, 155 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 8e0cf72..f2e04c0 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,4 +1,4 @@
ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_fbdev.o hibmc_ttm.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_fbdev.o hibmc_ttm.o
obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 9de3564..c133644 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -123,6 +123,12 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
return ret;
}
+ ret = hibmc_vdac_init(priv);
+ if (ret) {
+ DRM_ERROR("failed to init vdac: %d\n", ret);
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 87af1eb..b626caf 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -86,6 +86,7 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv,
unsigned int gate);
int hibmc_de_init(struct hibmc_drm_private *priv);
+int hibmc_vdac_init(struct hibmc_drm_private *priv);
int hibmc_fbdev_init(struct hibmc_drm_private *priv);
void hibmc_fbdev_fini(struct hibmc_drm_private *priv);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
new file mode 100644
index 0000000..d1f67a9
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
@@ -0,0 +1,147 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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 <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "hibmc_drm_drv.h"
+#include "hibmc_drm_regs.h"
+
+static int hibmc_connector_get_modes(struct drm_connector *connector)
+{
+ return drm_add_modes_noedid(connector, 800, 600);
+}
+
+static int hibmc_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+hibmc_connector_best_encoder(struct drm_connector *connector)
+{
+ return drm_encoder_find(connector->dev, connector->encoder_ids[0]);
+}
+
+static enum drm_connector_status hibmc_connector_detect(struct drm_connector
+ *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static const struct drm_connector_helper_funcs
+ hibmc_connector_helper_funcs = {
+ .get_modes = hibmc_connector_get_modes,
+ .mode_valid = hibmc_connector_mode_valid,
+ .best_encoder = hibmc_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs hibmc_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = hibmc_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 struct drm_connector *
+hibmc_connector_init(struct hibmc_drm_private *priv)
+{
+ struct drm_device *dev = priv->dev;
+ struct drm_connector *connector;
+ int ret;
+
+ connector = devm_kzalloc(dev->dev, sizeof(*connector), GFP_KERNEL);
+ if (!connector) {
+ DRM_ERROR("failed to alloc memory when init connector\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = drm_connector_init(dev, connector,
+ &hibmc_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA);
+ if (ret) {
+ DRM_ERROR("failed to init connector: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+ drm_connector_helper_add(connector,
+ &hibmc_connector_helper_funcs);
+
+ return connector;
+}
+
+static void hibmc_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ u32 reg;
+ struct drm_device *dev = encoder->dev;
+ struct hibmc_drm_private *priv = dev->dev_private;
+
+ reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE);
+ reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1);
+ reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1);
+ reg |= HIBMC_DISPLAY_CONTROL_FPEN(1);
+ reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1);
+ writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE);
+}
+
+static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = {
+ .mode_set = hibmc_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs hibmc_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+int hibmc_vdac_init(struct hibmc_drm_private *priv)
+{
+ struct drm_device *dev = priv->dev;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int ret;
+
+ connector = hibmc_connector_init(priv);
+ if (IS_ERR(connector)) {
+ DRM_ERROR("failed to create connector: %ld\n",
+ PTR_ERR(connector));
+ return PTR_ERR(connector);
+ }
+
+ encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL);
+ if (!encoder) {
+ DRM_ERROR("failed to alloc memory when init encoder\n");
+ return -ENOMEM;
+ }
+
+ encoder->possible_crtcs = 0x1;
+ ret = drm_encoder_init(dev, encoder, &hibmc_encoder_funcs,
+ DRM_MODE_ENCODER_DAC, NULL);
+ if (ret) {
+ DRM_ERROR("failed to init encoder: %d\n", ret);
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
--
1.9.1
^ permalink raw reply related
* [PATCH v7 4/7] drm/hisilicon/hibmc: Add support for display engine
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
Add display engine function, crtc/plane is initialized here.
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +-
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 477 ++++++++++++++++++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 69 +++-
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 6 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c | 6 +
5 files changed, 558 insertions(+), 2 deletions(-)
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index ff77a7e..8e0cf72 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,4 +1,4 @@
ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_fbdev.o hibmc_ttm.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_fbdev.o hibmc_ttm.o
obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
new file mode 100644
index 0000000..2a1386e
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -0,0 +1,477 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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 <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "hibmc_drm_drv.h"
+#include "hibmc_drm_regs.h"
+
+struct hibmc_display_panel_pll {
+ unsigned long M;
+ unsigned long N;
+ unsigned long OD;
+ unsigned long POD;
+};
+
+struct hibmc_dislay_pll_config {
+ unsigned long hdisplay;
+ unsigned long vdisplay;
+ u32 pll1_config_value;
+ u32 pll2_config_value;
+};
+
+static const struct hibmc_dislay_pll_config hibmc_pll_table[] = {
+ {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ},
+ {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ},
+ {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ},
+ {1280, 768, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ},
+ {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ},
+ {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
+ {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
+ {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ},
+ {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ},
+ {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ},
+};
+
+#define PADDING(align, data) (((data) + (align) - 1) & (~((align) - 1)))
+
+static int hibmc_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_crtc *crtc = state->crtc;
+ struct drm_crtc_state *crtc_state;
+ u32 src_w = state->src_w >> 16;
+ u32 src_h = state->src_h >> 16;
+
+ if (!crtc || !fb)
+ return 0;
+
+ crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ if (src_w != state->crtc_w || src_h != state->crtc_h) {
+ DRM_DEBUG_ATOMIC("scale not support\n");
+ return -EINVAL;
+ }
+
+ if (state->crtc_x < 0 || state->crtc_y < 0) {
+ DRM_DEBUG_ATOMIC("crtc_x/y of drm_plane state is invalid\n");
+ return -EINVAL;
+ }
+
+ if (state->crtc_x + state->crtc_w >
+ crtc_state->adjusted_mode.hdisplay ||
+ state->crtc_y + state->crtc_h >
+ crtc_state->adjusted_mode.vdisplay) {
+ DRM_DEBUG_ATOMIC("visible portion of plane is invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void hibmc_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ u32 reg;
+ int ret;
+ u64 gpu_addr = 0;
+ unsigned int line_l;
+ struct hibmc_drm_private *priv = plane->dev->dev_private;
+ struct hibmc_framebuffer *hibmc_fb;
+ struct hibmc_bo *bo;
+
+ if (!state->fb)
+ return;
+
+ hibmc_fb = to_hibmc_framebuffer(state->fb);
+ bo = gem_to_hibmc_bo(hibmc_fb->obj);
+ ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
+ if (ret) {
+ DRM_ERROR("failed to reserve ttm_bo: %d", ret);
+ return;
+ }
+
+ ret = hibmc_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
+ ttm_bo_unreserve(&bo->bo);
+ if (ret) {
+ DRM_ERROR("failed to pin hibmc_bo: %d", ret);
+ return;
+ }
+
+ writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS);
+
+ reg = state->fb->width * (state->fb->bits_per_pixel / 8);
+ /* now line_pad is 16 */
+ reg = PADDING(16, reg);
+
+ line_l = state->fb->width * state->fb->bits_per_pixel / 8;
+ line_l = PADDING(16, line_l);
+ writel(HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_WIDTH, reg) |
+ HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_OFFS, line_l),
+ priv->mmio + HIBMC_CRT_FB_WIDTH);
+
+ /* SET PIXEL FORMAT */
+ reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL);
+ reg &= ~HIBMC_CRT_DISP_CTL_FORMAT_MASK;
+ reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT,
+ state->fb->bits_per_pixel / 16);
+ writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL);
+}
+
+static const u32 channel_formats1[] = {
+ DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888
+};
+
+static struct drm_plane_funcs hibmc_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .set_property = drm_atomic_helper_plane_set_property,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = {
+ .atomic_check = hibmc_plane_atomic_check,
+ .atomic_update = hibmc_plane_atomic_update,
+};
+
+static struct drm_plane *hibmc_plane_init(struct hibmc_drm_private *priv)
+{
+ struct drm_device *dev = priv->dev;
+ struct drm_plane *plane;
+ int ret = 0;
+
+ plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
+ if (!plane) {
+ DRM_ERROR("failed to alloc memory when init plane\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ /*
+ * plane init
+ * TODO: Now only support primary plane, overlay planes
+ * need to do.
+ */
+ ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs,
+ channel_formats1,
+ ARRAY_SIZE(channel_formats1),
+ DRM_PLANE_TYPE_PRIMARY,
+ NULL);
+ if (ret) {
+ DRM_ERROR("failed to init plane: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
+ return plane;
+}
+
+static void hibmc_crtc_enable(struct drm_crtc *crtc)
+{
+ unsigned int reg;
+ struct hibmc_drm_private *priv = crtc->dev->dev_private;
+
+ hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+ /* Enable display power gate & LOCALMEM power gate*/
+ reg = readl(priv->mmio + HIBMC_CURRENT_GATE);
+ reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+ reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+ reg |= HIBMC_CURR_GATE_LOCALMEM(1);
+ reg |= HIBMC_CURR_GATE_DISPLAY(1);
+ hibmc_set_current_gate(priv, reg);
+ drm_crtc_vblank_on(crtc);
+}
+
+static void hibmc_crtc_disable(struct drm_crtc *crtc)
+{
+ unsigned int reg;
+ struct hibmc_drm_private *priv = crtc->dev->dev_private;
+
+ drm_crtc_vblank_off(crtc);
+
+ hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_SLEEP);
+
+ /* Enable display power gate & LOCALMEM power gate*/
+ reg = readl(priv->mmio + HIBMC_CURRENT_GATE);
+ reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+ reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+ reg |= HIBMC_CURR_GATE_LOCALMEM(0);
+ reg |= HIBMC_CURR_GATE_DISPLAY(0);
+ hibmc_set_current_gate(priv, reg);
+}
+
+static unsigned int format_pll_reg(void)
+{
+ unsigned int pllreg = 0;
+ struct hibmc_display_panel_pll pll = {0};
+
+ /*
+ * Note that all PLL's have the same format. Here,
+ * we just use Panel PLL parameter to work out the bit
+ * fields in the register.On returning a 32 bit number, the value can
+ * be applied to any PLL in the calling function.
+ */
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_BYPASS, 0);
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_POWER, 1);
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_INPUT, 0);
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_POD, pll.POD);
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_OD, pll.OD);
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_N, pll.N);
+ pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_M, pll.M);
+
+ return pllreg;
+}
+
+static void set_vclock_hisilicon(struct drm_device *dev, unsigned long pll)
+{
+ u32 val;
+ struct hibmc_drm_private *priv = dev->dev_private;
+
+ val = readl(priv->mmio + CRT_PLL1_HS);
+ val &= ~(CRT_PLL1_HS_OUTER_BYPASS(1));
+ writel(val, priv->mmio + CRT_PLL1_HS);
+
+ val = CRT_PLL1_HS_INTER_BYPASS(1) | CRT_PLL1_HS_POWERON(1);
+ writel(val, priv->mmio + CRT_PLL1_HS);
+
+ writel(pll, priv->mmio + CRT_PLL1_HS);
+
+ usleep_range(1000, 2000);
+
+ val = pll & ~(CRT_PLL1_HS_POWERON(1));
+ writel(val, priv->mmio + CRT_PLL1_HS);
+
+ usleep_range(1000, 2000);
+
+ val &= ~(CRT_PLL1_HS_INTER_BYPASS(1));
+ writel(val, priv->mmio + CRT_PLL1_HS);
+
+ usleep_range(1000, 2000);
+
+ val |= CRT_PLL1_HS_OUTER_BYPASS(1);
+ writel(val, priv->mmio + CRT_PLL1_HS);
+}
+
+static void get_pll_config(unsigned long x, unsigned long y,
+ u32 *pll1, u32 *pll2)
+{
+ int i;
+ int count = ARRAY_SIZE(hibmc_pll_table);
+
+ for (i = 0; i < count; i++) {
+ if (hibmc_pll_table[i].hdisplay == x &&
+ hibmc_pll_table[i].vdisplay == y) {
+ *pll1 = hibmc_pll_table[i].pll1_config_value;
+ *pll2 = hibmc_pll_table[i].pll2_config_value;
+ return;
+ }
+ }
+
+ /* if found none, we use default value */
+ *pll1 = CRT_PLL1_HS_25MHZ;
+ *pll2 = CRT_PLL2_HS_25MHZ;
+}
+
+/*
+ * This function takes care the extra registers and bit fields required to
+ * setup a mode in board.
+ * Explanation about Display Control register:
+ * FPGA only supports 7 predefined pixel clocks, and clock select is
+ * in bit 4:0 of new register 0x802a8.
+ */
+static unsigned int display_ctrl_adjust(struct drm_device *dev,
+ struct drm_display_mode *mode,
+ unsigned int ctrl)
+{
+ unsigned long x, y;
+ u32 pll1; /* bit[31:0] of PLL */
+ u32 pll2; /* bit[63:32] of PLL */
+ struct hibmc_drm_private *priv = dev->dev_private;
+
+ x = mode->hdisplay;
+ y = mode->vdisplay;
+
+ get_pll_config(x, y, &pll1, &pll2);
+ writel(pll2, priv->mmio + CRT_PLL2_HS);
+ set_vclock_hisilicon(dev, pll1);
+
+ /*
+ * Hisilicon has to set up the top-left and bottom-right
+ * registers as well.
+ * Note that normal chip only use those two register for
+ * auto-centering mode.
+ */
+ writel(HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_TL_TOP, 0) |
+ HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_TL_LEFT, 0),
+ priv->mmio + HIBMC_CRT_AUTO_CENTERING_TL);
+
+ writel(HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM, y - 1) |
+ HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_BR_RIGHT, x - 1),
+ priv->mmio + HIBMC_CRT_AUTO_CENTERING_BR);
+
+ /*
+ * Assume common fields in ctrl have been properly set before
+ * calling this function.
+ * This function only sets the extra fields in ctrl.
+ */
+
+ /* Set bit 25 of display controller: Select CRT or VGA clock */
+ ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK;
+ ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK;
+
+ ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(HIBMC_CRTSELECT_CRT);
+
+ /* clock_phase_polarity is 0 */
+ ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(0);
+
+ writel(ctrl, priv->mmio + HIBMC_CRT_DISP_CTL);
+
+ return ctrl;
+}
+
+static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+ unsigned int val;
+ struct drm_display_mode *mode = &crtc->state->mode;
+ struct drm_device *dev = crtc->dev;
+ struct hibmc_drm_private *priv = dev->dev_private;
+ int width = mode->hsync_end - mode->hsync_start;
+ int height = mode->vsync_end - mode->vsync_start;
+
+ writel(format_pll_reg(), priv->mmio + HIBMC_CRT_PLL_CTRL);
+ writel(HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_TOTAL, mode->htotal - 1) |
+ HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_DISP_END, mode->hdisplay - 1),
+ priv->mmio + HIBMC_CRT_HORZ_TOTAL);
+
+ writel(HIBMC_FIELD(HIBMC_CRT_HORZ_SYNC_WIDTH, width) |
+ HIBMC_FIELD(HIBMC_CRT_HORZ_SYNC_START, mode->hsync_start - 1),
+ priv->mmio + HIBMC_CRT_HORZ_SYNC);
+
+ writel(HIBMC_FIELD(HIBMC_CRT_VERT_TOTAL_TOTAL, mode->vtotal - 1) |
+ HIBMC_FIELD(HIBMC_CRT_VERT_TOTAL_DISP_END, mode->vdisplay - 1),
+ priv->mmio + HIBMC_CRT_VERT_TOTAL);
+
+ writel(HIBMC_FIELD(HIBMC_CRT_VERT_SYNC_HEIGHT, height) |
+ HIBMC_FIELD(HIBMC_CRT_VERT_SYNC_START, mode->vsync_start - 1),
+ priv->mmio + HIBMC_CRT_VERT_SYNC);
+
+ val = HIBMC_FIELD(HIBMC_CRT_DISP_CTL_VSYNC_PHASE, 0);
+ val |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_HSYNC_PHASE, 0);
+ val |= HIBMC_CRT_DISP_CTL_TIMING(1);
+ val |= HIBMC_CRT_DISP_CTL_PLANE(1);
+
+ display_ctrl_adjust(dev, mode, val);
+}
+
+static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ unsigned int reg;
+ struct drm_device *dev = crtc->dev;
+ struct hibmc_drm_private *priv = dev->dev_private;
+
+ hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+ /* Enable display power gate & LOCALMEM power gate*/
+ reg = readl(priv->mmio + HIBMC_CURRENT_GATE);
+ reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+ reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+ reg |= HIBMC_CURR_GATE_DISPLAY(1);
+ reg |= HIBMC_CURR_GATE_LOCALMEM(1);
+ hibmc_set_current_gate(priv, reg);
+
+ /* We can add more initialization as needed. */
+}
+
+static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ if (crtc->state->event)
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
+
+static const struct drm_crtc_funcs hibmc_crtc_funcs = {
+ .page_flip = drm_atomic_helper_page_flip,
+ .set_config = drm_atomic_helper_set_config,
+ .destroy = drm_crtc_cleanup,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
+ .enable = hibmc_crtc_enable,
+ .disable = hibmc_crtc_disable,
+ .mode_set_nofb = hibmc_crtc_mode_set_nofb,
+ .atomic_begin = hibmc_crtc_atomic_begin,
+ .atomic_flush = hibmc_crtc_atomic_flush,
+};
+
+int hibmc_de_init(struct hibmc_drm_private *priv)
+{
+ struct drm_device *dev = priv->dev;
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+ int ret;
+
+ plane = hibmc_plane_init(priv);
+ if (IS_ERR(plane)) {
+ DRM_ERROR("failed to create plane: %ld\n", PTR_ERR(plane));
+ return PTR_ERR(plane);
+ }
+
+ crtc = devm_kzalloc(dev->dev, sizeof(*crtc), GFP_KERNEL);
+ if (!crtc) {
+ DRM_ERROR("failed to alloc memory when init crtc\n");
+ return -ENOMEM;
+ }
+
+ ret = drm_crtc_init_with_planes(dev, crtc, plane,
+ NULL, &hibmc_crtc_funcs, NULL);
+ if (ret) {
+ DRM_ERROR("failed to init crtc: %d\n", ret);
+ return ret;
+ }
+
+ ret = drm_mode_crtc_set_gamma_size(crtc, 256);
+ if (ret) {
+ DRM_ERROR("failed to set gamma size: %d\n", ret);
+ return ret;
+ }
+ drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 4b52b29..9de3564 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -19,6 +19,9 @@
#include <linux/console.h>
#include <linux/module.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
#include "hibmc_drm_drv.h"
#include "hibmc_drm_regs.h"
@@ -44,7 +47,8 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
}
static struct drm_driver hibmc_driver = {
- .driver_features = DRIVER_GEM,
+ .driver_features = DRIVER_GEM | DRIVER_MODESET |
+ DRIVER_ATOMIC,
.fops = &hibmc_fops,
.name = "hibmc",
.date = "20160828",
@@ -62,11 +66,31 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
static int hibmc_pm_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct hibmc_drm_private *priv = drm_dev->dev_private;
+
+ drm_kms_helper_poll_disable(drm_dev);
+ priv->suspend_state = drm_atomic_helper_suspend(drm_dev);
+ if (IS_ERR(priv->suspend_state)) {
+ DRM_ERROR("drm_atomic_helper_suspend failed: %ld\n",
+ PTR_ERR(priv->suspend_state));
+ drm_kms_helper_poll_enable(drm_dev);
+ return PTR_ERR(priv->suspend_state);
+ }
+
return 0;
}
static int hibmc_pm_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct hibmc_drm_private *priv = drm_dev->dev_private;
+
+ drm_atomic_helper_resume(drm_dev, priv->suspend_state);
+ drm_kms_helper_poll_enable(drm_dev);
+
return 0;
}
@@ -75,6 +99,41 @@ static int hibmc_pm_resume(struct device *dev)
hibmc_pm_resume)
};
+static int hibmc_kms_init(struct hibmc_drm_private *priv)
+{
+ int ret;
+
+ drm_mode_config_init(priv->dev);
+ priv->mode_config_initialized = true;
+
+ 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;
+
+ priv->dev->mode_config.fb_base = priv->fb_base;
+ priv->dev->mode_config.preferred_depth = 24;
+ priv->dev->mode_config.prefer_shadow = 0;
+
+ priv->dev->mode_config.funcs = (void *)&hibmc_mode_funcs;
+
+ ret = hibmc_de_init(priv);
+ if (ret) {
+ DRM_ERROR("failed to init de: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+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;
+ }
+}
+
/*
* It can operate in one of three modes: 0, 1 or Sleep.
*/
@@ -203,6 +262,7 @@ static int hibmc_unload(struct drm_device *dev)
struct hibmc_drm_private *priv = dev->dev_private;
hibmc_fbdev_fini(priv);
+ hibmc_kms_fini(priv);
hibmc_mm_fini(priv);
dev->dev_private = NULL;
return 0;
@@ -229,6 +289,13 @@ static int hibmc_load(struct drm_device *dev)
if (ret)
goto err;
+ ret = hibmc_kms_init(priv);
+ if (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);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index d283d66..87af1eb 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -20,6 +20,7 @@
#define HIBMC_DRM_DRV_H
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem.h>
#include <drm/ttm/ttm_bo_driver.h>
@@ -44,6 +45,8 @@ struct hibmc_drm_private {
/* drm */
struct drm_device *dev;
+ bool mode_config_initialized;
+ struct drm_atomic_state *suspend_state;
/* ttm */
struct drm_global_reference mem_global_ref;
@@ -82,6 +85,7 @@ void hibmc_set_power_mode(struct hibmc_drm_private *priv,
void hibmc_set_current_gate(struct hibmc_drm_private *priv,
unsigned int gate);
+int hibmc_de_init(struct hibmc_drm_private *priv);
int hibmc_fbdev_init(struct hibmc_drm_private *priv);
void hibmc_fbdev_fini(struct hibmc_drm_private *priv);
@@ -103,4 +107,6 @@ int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
u32 handle, u64 *offset);
int hibmc_mmap(struct file *filp, struct vm_area_struct *vma);
+extern const struct drm_mode_config_funcs hibmc_mode_funcs;
+
#endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
index 3ff65f4..e76abf6 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
@@ -550,3 +550,9 @@ struct hibmc_framebuffer *
}
return &hibmc_fb->fb;
}
+
+const struct drm_mode_config_funcs hibmc_mode_funcs = {
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+ .fb_create = hibmc_user_framebuffer_create,
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v7 3/7] drm/hisilicon/hibmc: Add support for frame buffer
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
Add support for fbdev and kms fb management.
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +-
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 7 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 24 ++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 267 ++++++++++++++++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c | 67 ++++++
5 files changed, 366 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 19ed0ef..ff77a7e 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,4 +1,4 @@
ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_ttm.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_fbdev.o hibmc_ttm.o
obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 521f69f..4b52b29 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -202,6 +202,7 @@ static int hibmc_unload(struct drm_device *dev)
{
struct hibmc_drm_private *priv = dev->dev_private;
+ hibmc_fbdev_fini(priv);
hibmc_mm_fini(priv);
dev->dev_private = NULL;
return 0;
@@ -228,6 +229,12 @@ static int hibmc_load(struct drm_device *dev)
if (ret)
goto err;
+ ret = hibmc_fbdev_init(priv);
+ if (ret) {
+ DRM_ERROR("failed to initialize fbdev: %d\n", ret);
+ goto err;
+ }
+
return 0;
err:
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index dcd304d..d283d66 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -20,9 +20,21 @@
#define HIBMC_DRM_DRV_H
#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
#include <drm/drm_gem.h>
#include <drm/ttm/ttm_bo_driver.h>
+struct hibmc_framebuffer {
+ struct drm_framebuffer fb;
+ struct drm_gem_object *obj;
+};
+
+struct hibmc_fbdev {
+ struct drm_fb_helper helper;
+ struct hibmc_framebuffer *fb;
+ int size;
+};
+
struct hibmc_drm_private {
/* hw */
void __iomem *mmio;
@@ -39,9 +51,13 @@ struct hibmc_drm_private {
struct ttm_bo_device bdev;
bool initialized;
+ /* fbdev */
+ struct hibmc_fbdev *fbdev;
bool mm_inited;
};
+#define to_hibmc_framebuffer(x) container_of(x, struct hibmc_framebuffer, fb)
+
struct hibmc_bo {
struct ttm_buffer_object bo;
struct ttm_placement placement;
@@ -66,8 +82,16 @@ void hibmc_set_power_mode(struct hibmc_drm_private *priv,
void hibmc_set_current_gate(struct hibmc_drm_private *priv,
unsigned int gate);
+int hibmc_fbdev_init(struct hibmc_drm_private *priv);
+void hibmc_fbdev_fini(struct hibmc_drm_private *priv);
+
int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel,
struct drm_gem_object **obj);
+struct hibmc_framebuffer *
+hibmc_framebuffer_init(struct drm_device *dev,
+ const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj);
+
int hibmc_mm_init(struct hibmc_drm_private *hibmc);
void hibmc_mm_fini(struct hibmc_drm_private *hibmc);
int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
new file mode 100644
index 0000000..9b06967
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
@@ -0,0 +1,267 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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 <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include "hibmc_drm_drv.h"
+
+static int hibmcfb_create_object(
+ struct hibmc_drm_private *priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object **gobj_p)
+{
+ struct drm_gem_object *gobj;
+ struct drm_device *dev = priv->dev;
+ u32 size;
+ int ret = 0;
+
+ size = mode_cmd->pitches[0] * mode_cmd->height;
+ ret = hibmc_gem_create(dev, size, true, &gobj);
+ if (ret)
+ return ret;
+
+ *gobj_p = gobj;
+ return ret;
+}
+
+static struct fb_ops hibmc_drm_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_fillrect = drm_fb_helper_sys_fillrect,
+ .fb_copyarea = drm_fb_helper_sys_copyarea,
+ .fb_imageblit = drm_fb_helper_sys_imageblit,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int hibmc_drm_fb_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct hibmc_fbdev *hi_fbdev =
+ container_of(helper, struct hibmc_fbdev, helper);
+ struct hibmc_drm_private *priv = helper->dev->dev_private;
+ struct fb_info *info;
+ struct drm_mode_fb_cmd2 mode_cmd;
+ struct drm_gem_object *gobj = NULL;
+ int ret = 0;
+ int ret1;
+ size_t size;
+ unsigned int bytes_per_pixel;
+ struct hibmc_bo *bo = NULL;
+
+ DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n",
+ sizes->surface_width, sizes->surface_height,
+ sizes->surface_bpp);
+ sizes->surface_depth = 32;
+
+ bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = mode_cmd.width * bytes_per_pixel;
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ size = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height);
+
+ ret = hibmcfb_create_object(priv, &mode_cmd, &gobj);
+ if (ret) {
+ DRM_ERROR("failed to create fbcon backing object: %d\n", ret);
+ return -ENOMEM;
+ }
+
+ bo = gem_to_hibmc_bo(gobj);
+
+ ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
+ if (ret) {
+ DRM_ERROR("failed to reserve ttm_bo: %d\n", ret);
+ goto out_unref_gem;
+ }
+
+ ret = hibmc_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
+ if (ret) {
+ DRM_ERROR("failed to pin fbcon: %d\n", ret);
+ goto out_unreserve_ttm_bo;
+ }
+
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+ if (ret) {
+ DRM_ERROR("failed to kmap fbcon: %d\n", ret);
+ goto out_unpin_bo;
+ }
+ ttm_bo_unreserve(&bo->bo);
+
+ info = drm_fb_helper_alloc_fbi(helper);
+ if (IS_ERR(info)) {
+ ret = PTR_ERR(info);
+ DRM_ERROR("failed to allocate fbi: %d\n", ret);
+ goto out_release_fbi;
+ }
+
+ info->par = hi_fbdev;
+
+ hi_fbdev->fb = hibmc_framebuffer_init(priv->dev, &mode_cmd, gobj);
+ if (IS_ERR(hi_fbdev->fb)) {
+ ret = PTR_ERR(info);
+ DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
+ goto out_release_fbi;
+ }
+
+ priv->fbdev->size = size;
+ hi_fbdev->helper.fb = &hi_fbdev->fb->fb;
+
+ strcpy(info->fix.id, "hibmcdrmfb");
+
+ info->flags = FBINFO_DEFAULT;
+ info->fbops = &hibmc_drm_fb_ops;
+
+ drm_fb_helper_fill_fix(info, hi_fbdev->fb->fb.pitches[0],
+ hi_fbdev->fb->fb.depth);
+ drm_fb_helper_fill_var(info, &priv->fbdev->helper, sizes->fb_width,
+ sizes->fb_height);
+
+ info->screen_base = bo->kmap.virtual;
+ info->screen_size = size;
+
+ info->fix.smem_start = bo->bo.mem.bus.offset + bo->bo.mem.bus.base;
+ info->fix.smem_len = size;
+ return 0;
+
+out_release_fbi:
+ drm_fb_helper_release_fbi(helper);
+ ret1 = ttm_bo_reserve(&bo->bo, true, false, NULL);
+ if (ret1) {
+ DRM_ERROR("failed to rsv ttm_bo when release fbi: %d\n", ret1);
+ goto out_unref_gem;
+ }
+ ttm_bo_kunmap(&bo->kmap);
+out_unpin_bo:
+ hibmc_bo_unpin(bo);
+out_unreserve_ttm_bo:
+ ttm_bo_unreserve(&bo->bo);
+out_unref_gem:
+ drm_gem_object_unreference_unlocked(gobj);
+
+ return ret;
+}
+
+static void hibmc_fbdev_destroy(struct hibmc_fbdev *fbdev)
+{
+ struct hibmc_framebuffer *gfb = fbdev->fb;
+ struct drm_fb_helper *fbh = &fbdev->helper;
+
+ drm_fb_helper_unregister_fbi(fbh);
+ drm_fb_helper_release_fbi(fbh);
+
+ drm_fb_helper_fini(fbh);
+
+ if (gfb)
+ drm_framebuffer_unreference(&gfb->fb);
+}
+
+static const struct drm_fb_helper_funcs hibmc_fbdev_helper_funcs = {
+ .fb_probe = hibmc_drm_fb_create,
+};
+
+int hibmc_fbdev_init(struct hibmc_drm_private *priv)
+{
+ int ret;
+ struct fb_var_screeninfo *var;
+ struct fb_fix_screeninfo *fix;
+ struct hibmc_fbdev *hifbdev;
+
+ hifbdev = devm_kzalloc(priv->dev->dev, sizeof(*hifbdev), GFP_KERNEL);
+ if (!hifbdev) {
+ DRM_ERROR("failed to allocate hibmc_fbdev\n");
+ return -ENOMEM;
+ }
+
+ priv->fbdev = hifbdev;
+ drm_fb_helper_prepare(priv->dev, &hifbdev->helper,
+ &hibmc_fbdev_helper_funcs);
+
+ /* Now just one crtc and one channel */
+ ret = drm_fb_helper_init(priv->dev,
+ &hifbdev->helper, 1, 1);
+ if (ret) {
+ DRM_ERROR("failed to initialize fb helper: %d\n", ret);
+ return ret;
+ }
+
+ ret = drm_fb_helper_single_add_all_connectors(&hifbdev->helper);
+ if (ret) {
+ DRM_ERROR("failed to add all connectors: %d\n", ret);
+ goto fini;
+ }
+
+ ret = drm_fb_helper_initial_config(&hifbdev->helper, 16);
+ if (ret) {
+ DRM_ERROR("failed to setup initial conn config: %d\n", ret);
+ goto fini;
+ }
+
+ var = &hifbdev->helper.fbdev->var;
+ fix = &hifbdev->helper.fbdev->fix;
+
+ DRM_DEBUG_DRIVER("Member of info->var is :\n"
+ "xres=%d\n"
+ "yres=%d\n"
+ "xres_virtual=%d\n"
+ "yres_virtual=%d\n"
+ "xoffset=%d\n"
+ "yoffset=%d\n"
+ "bits_per_pixel=%d\n"
+ "...\n", var->xres, var->yres, var->xres_virtual,
+ var->yres_virtual, var->xoffset, var->yoffset,
+ var->bits_per_pixel);
+ DRM_DEBUG_DRIVER("Member of info->fix is :\n"
+ "smem_start=%lx\n"
+ "smem_len=%d\n"
+ "type=%d\n"
+ "type_aux=%d\n"
+ "visual=%d\n"
+ "xpanstep=%d\n"
+ "ypanstep=%d\n"
+ "ywrapstep=%d\n"
+ "line_length=%d\n"
+ "accel=%d\n"
+ "capabilities=%d\n"
+ "...\n", fix->smem_start, fix->smem_len, fix->type,
+ fix->type_aux, fix->visual, fix->xpanstep,
+ fix->ypanstep, fix->ywrapstep, fix->line_length,
+ fix->accel, fix->capabilities);
+
+ return 0;
+
+fini:
+ drm_fb_helper_fini(&hifbdev->helper);
+ return ret;
+}
+
+void hibmc_fbdev_fini(struct hibmc_drm_private *priv)
+{
+ if (!priv->fbdev)
+ return;
+
+ hibmc_fbdev_destroy(priv->fbdev);
+ priv->fbdev = NULL;
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
index 036d3ac..3ff65f4 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
@@ -16,6 +16,7 @@
*
*/
+#include <drm/drm_atomic_helper.h>
#include <ttm/ttm_page_alloc.h>
#include "hibmc_drm_drv.h"
@@ -483,3 +484,69 @@ int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
drm_gem_object_unreference_unlocked(obj);
return 0;
}
+
+static void hibmc_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct hibmc_framebuffer *hibmc_fb = to_hibmc_framebuffer(fb);
+
+ drm_gem_object_unreference_unlocked(hibmc_fb->obj);
+ drm_framebuffer_cleanup(fb);
+ kfree(hibmc_fb);
+}
+
+static const struct drm_framebuffer_funcs hibmc_fb_funcs = {
+ .destroy = hibmc_user_framebuffer_destroy,
+};
+
+struct hibmc_framebuffer *
+hibmc_framebuffer_init(struct drm_device *dev,
+ const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ struct hibmc_framebuffer *hibmc_fb;
+ int ret;
+
+ hibmc_fb = kzalloc(sizeof(*hibmc_fb), GFP_KERNEL);
+ if (!hibmc_fb) {
+ DRM_ERROR("failed to allocate hibmc_fb\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drm_helper_mode_fill_fb_struct(&hibmc_fb->fb, mode_cmd);
+ hibmc_fb->obj = obj;
+ ret = drm_framebuffer_init(dev, &hibmc_fb->fb, &hibmc_fb_funcs);
+ if (ret) {
+ DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
+ kfree(hibmc_fb);
+ return ERR_PTR(ret);
+ }
+
+ return hibmc_fb;
+}
+
+static struct drm_framebuffer *
+hibmc_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *filp,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_gem_object *obj;
+ struct hibmc_framebuffer *hibmc_fb;
+
+ DRM_DEBUG_DRIVER("%dx%d, format %c%c%c%c\n",
+ mode_cmd->width, mode_cmd->height,
+ (mode_cmd->pixel_format) & 0xff,
+ (mode_cmd->pixel_format >> 8) & 0xff,
+ (mode_cmd->pixel_format >> 16) & 0xff,
+ (mode_cmd->pixel_format >> 24) & 0xff);
+
+ obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
+ if (!obj)
+ return ERR_PTR(-ENOENT);
+
+ hibmc_fb = hibmc_framebuffer_init(dev, mode_cmd, obj);
+ if (IS_ERR(hibmc_fb)) {
+ drm_gem_object_unreference_unlocked(obj);
+ return ERR_PTR((long)hibmc_fb);
+ }
+ return &hibmc_fb->fb;
+}
--
1.9.1
^ permalink raw reply related
* [PATCH v7 2/7] drm/hisilicon/hibmc: Add video memory management
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
Hibmc have 32m video memory which can be accessed through PCIe by host,
we use ttm to manage these memory.
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +-
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 14 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 41 ++
drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c | 485 ++++++++++++++++++++++++
4 files changed, 541 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 47962a0..19ed0ef 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,4 +1,4 @@
ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_ttm.o
obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 6d20580..521f69f 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -28,6 +28,7 @@
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.compat_ioctl = drm_compat_ioctl,
+ .mmap = hibmc_mmap,
.poll = drm_poll,
.read = drm_read,
.llseek = no_llseek,
@@ -43,6 +44,7 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
}
static struct drm_driver hibmc_driver = {
+ .driver_features = DRIVER_GEM,
.fops = &hibmc_fops,
.name = "hibmc",
.date = "20160828",
@@ -52,6 +54,10 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = hibmc_enable_vblank,
.disable_vblank = hibmc_disable_vblank,
+ .gem_free_object_unlocked = hibmc_gem_free_object,
+ .dumb_create = hibmc_dumb_create,
+ .dumb_map_offset = hibmc_dumb_mmap_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
};
static int hibmc_pm_suspend(struct device *dev)
@@ -194,6 +200,10 @@ static int hibmc_hw_init(struct hibmc_drm_private *priv)
static int hibmc_unload(struct drm_device *dev)
{
+ struct hibmc_drm_private *priv = dev->dev_private;
+
+ hibmc_mm_fini(priv);
+ dev->dev_private = NULL;
return 0;
}
@@ -214,6 +224,10 @@ static int hibmc_load(struct drm_device *dev)
if (ret)
goto err;
+ ret = hibmc_mm_init(priv);
+ if (ret)
+ goto err;
+
return 0;
err:
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 840cd5a..dcd304d 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -20,6 +20,8 @@
#define HIBMC_DRM_DRV_H
#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/ttm/ttm_bo_driver.h>
struct hibmc_drm_private {
/* hw */
@@ -31,11 +33,50 @@ struct hibmc_drm_private {
/* drm */
struct drm_device *dev;
+ /* ttm */
+ struct drm_global_reference mem_global_ref;
+ struct ttm_bo_global_ref bo_global_ref;
+ struct ttm_bo_device bdev;
+ bool initialized;
+
+ bool mm_inited;
+};
+
+struct hibmc_bo {
+ struct ttm_buffer_object bo;
+ struct ttm_placement placement;
+ struct ttm_bo_kmap_obj kmap;
+ struct drm_gem_object gem;
+ struct ttm_place placements[3];
+ int pin_count;
};
+static inline struct hibmc_bo *hibmc_bo(struct ttm_buffer_object *bo)
+{
+ return container_of(bo, struct hibmc_bo, bo);
+}
+
+static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
+{
+ return container_of(gem, struct hibmc_bo, gem);
+}
+
void hibmc_set_power_mode(struct hibmc_drm_private *priv,
unsigned int power_mode);
void hibmc_set_current_gate(struct hibmc_drm_private *priv,
unsigned int gate);
+int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel,
+ struct drm_gem_object **obj);
+int hibmc_mm_init(struct hibmc_drm_private *hibmc);
+void hibmc_mm_fini(struct hibmc_drm_private *hibmc);
+int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr);
+int hibmc_bo_unpin(struct hibmc_bo *bo);
+void hibmc_gem_free_object(struct drm_gem_object *obj);
+int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
+ u32 handle, u64 *offset);
+int hibmc_mmap(struct file *filp, struct vm_area_struct *vma);
+
#endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
new file mode 100644
index 0000000..036d3ac
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
@@ -0,0 +1,485 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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 <ttm/ttm_page_alloc.h>
+
+#include "hibmc_drm_drv.h"
+
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
+
+static inline struct hibmc_drm_private *
+hibmc_bdev(struct ttm_bo_device *bd)
+{
+ return container_of(bd, struct hibmc_drm_private, bdev);
+}
+
+static int
+hibmc_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+ return ttm_mem_global_init(ref->object);
+}
+
+static void
+hibmc_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+ ttm_mem_global_release(ref->object);
+}
+
+static int hibmc_ttm_global_init(struct hibmc_drm_private *hibmc)
+{
+ int ret;
+
+ hibmc->mem_global_ref.global_type = DRM_GLOBAL_TTM_MEM;
+ hibmc->mem_global_ref.size = sizeof(struct ttm_mem_global);
+ hibmc->mem_global_ref.init = &hibmc_ttm_mem_global_init;
+ hibmc->mem_global_ref.release = &hibmc_ttm_mem_global_release;
+ ret = drm_global_item_ref(&hibmc->mem_global_ref);
+ if (ret) {
+ DRM_ERROR("could not get ref on ttm global: %d\n", ret);
+ return ret;
+ }
+
+ hibmc->bo_global_ref.mem_glob =
+ hibmc->mem_global_ref.object;
+ hibmc->bo_global_ref.ref.global_type = DRM_GLOBAL_TTM_BO;
+ hibmc->bo_global_ref.ref.size = sizeof(struct ttm_bo_global);
+ hibmc->bo_global_ref.ref.init = &ttm_bo_global_init;
+ hibmc->bo_global_ref.ref.release = &ttm_bo_global_release;
+ ret = drm_global_item_ref(&hibmc->bo_global_ref.ref);
+ if (ret) {
+ DRM_ERROR("failed setting up TTM BO subsystem: %d\n", ret);
+ drm_global_item_unref(&hibmc->mem_global_ref);
+ return ret;
+ }
+ return 0;
+}
+
+static void
+hibmc_ttm_global_release(struct hibmc_drm_private *hibmc)
+{
+ drm_global_item_unref(&hibmc->bo_global_ref.ref);
+ drm_global_item_unref(&hibmc->mem_global_ref);
+ hibmc->mem_global_ref.release = NULL;
+}
+
+static void hibmc_bo_ttm_destroy(struct ttm_buffer_object *tbo)
+{
+ struct hibmc_bo *bo = container_of(tbo, struct hibmc_bo, bo);
+
+ drm_gem_object_release(&bo->gem);
+ kfree(bo);
+}
+
+static bool hibmc_ttm_bo_is_hibmc_bo(struct ttm_buffer_object *bo)
+{
+ return bo->destroy == &hibmc_bo_ttm_destroy;
+}
+
+static int
+hibmc_bo_init_mem_type(struct ttm_bo_device *bdev, u32 type,
+ struct ttm_mem_type_manager *man)
+{
+ switch (type) {
+ case TTM_PL_SYSTEM:
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_MASK_CACHING;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ case TTM_PL_VRAM:
+ man->func = &ttm_bo_manager_func;
+ man->flags = TTM_MEMTYPE_FLAG_FIXED |
+ TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_WC;
+ man->default_caching = TTM_PL_FLAG_WC;
+ break;
+ default:
+ DRM_ERROR("unsupported memory type %u\n", type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void hibmc_ttm_placement(struct hibmc_bo *bo, int domain)
+{
+ u32 count = 0;
+ u32 i;
+
+ bo->placement.placement = bo->placements;
+ bo->placement.busy_placement = bo->placements;
+ if (domain & TTM_PL_FLAG_VRAM)
+ bo->placements[count++].flags = TTM_PL_FLAG_WC |
+ TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+ if (domain & TTM_PL_FLAG_SYSTEM)
+ bo->placements[count++].flags = TTM_PL_MASK_CACHING |
+ TTM_PL_FLAG_SYSTEM;
+ if (!count)
+ bo->placements[count++].flags = TTM_PL_MASK_CACHING |
+ TTM_PL_FLAG_SYSTEM;
+
+ bo->placement.num_placement = count;
+ bo->placement.num_busy_placement = count;
+ for (i = 0; i < count; i++) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
+ }
+}
+
+static void
+hibmc_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+{
+ struct hibmc_bo *hibmcbo = hibmc_bo(bo);
+
+ if (!hibmc_ttm_bo_is_hibmc_bo(bo))
+ return;
+
+ hibmc_ttm_placement(hibmcbo, TTM_PL_FLAG_SYSTEM);
+ *pl = hibmcbo->placement;
+}
+
+static int hibmc_bo_verify_access(struct ttm_buffer_object *bo,
+ struct file *filp)
+{
+ struct hibmc_bo *hibmcbo = hibmc_bo(bo);
+
+ return drm_vma_node_verify_access(&hibmcbo->gem.vma_node,
+ filp->private_data);
+}
+
+static int hibmc_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+ struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
+ struct hibmc_drm_private *hibmc = hibmc_bdev(bdev);
+
+ mem->bus.addr = NULL;
+ mem->bus.offset = 0;
+ mem->bus.size = mem->num_pages << PAGE_SHIFT;
+ mem->bus.base = 0;
+ mem->bus.is_iomem = false;
+ if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
+ return -EINVAL;
+ switch (mem->mem_type) {
+ case TTM_PL_SYSTEM:
+ /* system memory */
+ return 0;
+ case TTM_PL_VRAM:
+ mem->bus.offset = mem->start << PAGE_SHIFT;
+ mem->bus.base = pci_resource_start(hibmc->dev->pdev, 0);
+ mem->bus.is_iomem = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void hibmc_ttm_backend_destroy(struct ttm_tt *tt)
+{
+ ttm_tt_fini(tt);
+ kfree(tt);
+}
+
+static struct ttm_backend_func hibmc_tt_backend_func = {
+ .destroy = &hibmc_ttm_backend_destroy,
+};
+
+static struct ttm_tt *hibmc_ttm_tt_create(struct ttm_bo_device *bdev,
+ unsigned long size,
+ u32 page_flags,
+ struct page *dummy_read_page)
+{
+ struct ttm_tt *tt;
+ int ret;
+
+ tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ if (!tt) {
+ DRM_ERROR("failed to allocate ttm_tt\n");
+ return NULL;
+ }
+ tt->func = &hibmc_tt_backend_func;
+ ret = ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page);
+ if (ret) {
+ DRM_ERROR("failed to initialize ttm_tt: %d\n", ret);
+ kfree(tt);
+ return NULL;
+ }
+ return tt;
+}
+
+static int hibmc_ttm_tt_populate(struct ttm_tt *ttm)
+{
+ return ttm_pool_populate(ttm);
+}
+
+static void hibmc_ttm_tt_unpopulate(struct ttm_tt *ttm)
+{
+ ttm_pool_unpopulate(ttm);
+}
+
+struct ttm_bo_driver hibmc_bo_driver = {
+ .ttm_tt_create = hibmc_ttm_tt_create,
+ .ttm_tt_populate = hibmc_ttm_tt_populate,
+ .ttm_tt_unpopulate = hibmc_ttm_tt_unpopulate,
+ .init_mem_type = hibmc_bo_init_mem_type,
+ .evict_flags = hibmc_bo_evict_flags,
+ .move = NULL,
+ .verify_access = hibmc_bo_verify_access,
+ .io_mem_reserve = &hibmc_ttm_io_mem_reserve,
+ .io_mem_free = NULL,
+ .lru_tail = &ttm_bo_default_lru_tail,
+ .swap_lru_tail = &ttm_bo_default_swap_lru_tail,
+};
+
+int hibmc_mm_init(struct hibmc_drm_private *hibmc)
+{
+ int ret;
+ struct drm_device *dev = hibmc->dev;
+ struct ttm_bo_device *bdev = &hibmc->bdev;
+
+ ret = hibmc_ttm_global_init(hibmc);
+ if (ret)
+ return ret;
+
+ ret = ttm_bo_device_init(&hibmc->bdev,
+ hibmc->bo_global_ref.ref.object,
+ &hibmc_bo_driver,
+ dev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
+ true);
+ if (ret) {
+ hibmc_ttm_global_release(hibmc);
+ DRM_ERROR("error initializing bo driver: %d\n", ret);
+ return ret;
+ }
+
+ ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
+ hibmc->fb_size >> PAGE_SHIFT);
+ if (ret) {
+ hibmc_ttm_global_release(hibmc);
+ DRM_ERROR("failed ttm VRAM init: %d\n", ret);
+ return ret;
+ }
+
+ hibmc->mm_inited = true;
+ return 0;
+}
+
+void hibmc_mm_fini(struct hibmc_drm_private *hibmc)
+{
+ if (!hibmc->mm_inited)
+ return;
+
+ ttm_bo_device_release(&hibmc->bdev);
+ hibmc_ttm_global_release(hibmc);
+ hibmc->mm_inited = false;
+}
+
+static void hibmc_bo_unref(struct hibmc_bo **bo)
+{
+ struct ttm_buffer_object *tbo;
+
+ if ((*bo) == NULL)
+ return;
+
+ tbo = &((*bo)->bo);
+ ttm_bo_unref(&tbo);
+ *bo = NULL;
+}
+
+int hibmc_bo_create(struct drm_device *dev, int size, int align,
+ u32 flags, struct hibmc_bo **phibmcbo)
+{
+ struct hibmc_drm_private *hibmc = dev->dev_private;
+ struct hibmc_bo *hibmcbo;
+ size_t acc_size;
+ int ret;
+
+ hibmcbo = kzalloc(sizeof(*hibmcbo), GFP_KERNEL);
+ if (!hibmcbo) {
+ DRM_ERROR("failed to allocate hibmcbo\n");
+ return -ENOMEM;
+ }
+ ret = drm_gem_object_init(dev, &hibmcbo->gem, size);
+ if (ret) {
+ DRM_ERROR("failed to initialize drm gem object: %d\n", ret);
+ kfree(hibmcbo);
+ return ret;
+ }
+
+ hibmcbo->bo.bdev = &hibmc->bdev;
+
+ hibmc_ttm_placement(hibmcbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
+
+ acc_size = ttm_bo_dma_acc_size(&hibmc->bdev, size,
+ sizeof(struct hibmc_bo));
+
+ ret = ttm_bo_init(&hibmc->bdev, &hibmcbo->bo, size,
+ ttm_bo_type_device, &hibmcbo->placement,
+ align >> PAGE_SHIFT, false, NULL, acc_size,
+ NULL, NULL, hibmc_bo_ttm_destroy);
+ if (ret) {
+ hibmc_bo_unref(&hibmcbo);
+ DRM_ERROR("failed to initialize ttm_bo: %d\n", ret);
+ return ret;
+ }
+
+ *phibmcbo = hibmcbo;
+ return 0;
+}
+
+int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr)
+{
+ int i, ret;
+
+ if (bo->pin_count) {
+ bo->pin_count++;
+ if (gpu_addr)
+ *gpu_addr = bo->bo.offset;
+ return 0;
+ }
+
+ hibmc_ttm_placement(bo, pl_flag);
+ for (i = 0; i < bo->placement.num_placement; i++)
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+ if (ret)
+ return ret;
+
+ bo->pin_count = 1;
+ if (gpu_addr)
+ *gpu_addr = bo->bo.offset;
+ return 0;
+}
+
+int hibmc_bo_unpin(struct hibmc_bo *bo)
+{
+ int i, ret;
+
+ if (!bo->pin_count) {
+ DRM_ERROR("unpin bad %p\n", bo);
+ return 0;
+ }
+ bo->pin_count--;
+ if (bo->pin_count)
+ return 0;
+
+ for (i = 0; i < bo->placement.num_placement ; i++)
+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+ if (ret) {
+ DRM_ERROR("validate failed for unpin: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int hibmc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *file_priv;
+ struct hibmc_drm_private *hibmc;
+
+ if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+ return -EINVAL;
+
+ file_priv = filp->private_data;
+ hibmc = file_priv->minor->dev->dev_private;
+ return ttm_bo_mmap(filp, vma, &hibmc->bdev);
+}
+
+int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel,
+ struct drm_gem_object **obj)
+{
+ struct hibmc_bo *hibmcbo;
+ int ret;
+
+ *obj = NULL;
+
+ size = PAGE_ALIGN(size);
+ if (size == 0) {
+ DRM_ERROR("error: zero size\n");
+ return -EINVAL;
+ }
+
+ ret = hibmc_bo_create(dev, size, 0, 0, &hibmcbo);
+ if (ret) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("failed to allocate GEM object: %d\n", ret);
+ return ret;
+ }
+ *obj = &hibmcbo->gem;
+ return 0;
+}
+
+int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct drm_gem_object *gobj;
+ u32 handle;
+ int ret;
+
+ args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 16);
+ args->size = args->pitch * args->height;
+
+ ret = hibmc_gem_create(dev, args->size, false,
+ &gobj);
+ if (ret) {
+ DRM_ERROR("failed to create GEM object: %d\n", ret);
+ return ret;
+ }
+
+ ret = drm_gem_handle_create(file, gobj, &handle);
+ drm_gem_object_unreference_unlocked(gobj);
+ if (ret) {
+ DRM_ERROR("failed to unreference GEM object: %d\n", ret);
+ return ret;
+ }
+
+ args->handle = handle;
+ return 0;
+}
+
+void hibmc_gem_free_object(struct drm_gem_object *obj)
+{
+ struct hibmc_bo *hibmcbo = gem_to_hibmc_bo(obj);
+
+ hibmc_bo_unref(&hibmcbo);
+}
+
+static u64 hibmc_bo_mmap_offset(struct hibmc_bo *bo)
+{
+ return drm_vma_node_offset_addr(&bo->bo.vma_node);
+}
+
+int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
+ u32 handle, u64 *offset)
+{
+ struct drm_gem_object *obj;
+ struct hibmc_bo *bo;
+
+ obj = drm_gem_object_lookup(file, handle);
+ if (!obj)
+ return -ENOENT;
+
+ bo = gem_to_hibmc_bo(obj);
+ *offset = hibmc_bo_mmap_offset(bo);
+
+ drm_gem_object_unreference_unlocked(obj);
+ return 0;
+}
--
1.9.1
^ permalink raw reply related
* [PATCH v7 1/7] drm/hisilicon/hibmc: Add hisilicon hibmc drm master driver
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479303831-74134-1-git-send-email-zourongrong@gmail.com>
Add DRM master driver for Hisilicon Hibmc SoC which used for
Out-of-band management. Blow is the general hardware connection,
both the Hibmc and the host CPU are on the same mother board.
+----------+ +----------+
| | PCIe | Hibmc |
|host CPU( |<----->| display |
|arm64,x86)| |subsystem |
+----------+ +----------+
Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
drivers/gpu/drm/hisilicon/Kconfig | 1 +
drivers/gpu/drm/hisilicon/Makefile | 1 +
drivers/gpu/drm/hisilicon/hibmc/Kconfig | 9 +
drivers/gpu/drm/hisilicon/hibmc/Makefile | 4 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 308 +++++++++++++++++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 41 +++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h | 196 +++++++++++++++
7 files changed, 560 insertions(+)
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Kconfig
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Makefile
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig
index 558c61b..2fd2724 100644
--- a/drivers/gpu/drm/hisilicon/Kconfig
+++ b/drivers/gpu/drm/hisilicon/Kconfig
@@ -2,4 +2,5 @@
# hisilicon drm device configuration.
# Please keep this list sorted alphabetically
+source "drivers/gpu/drm/hisilicon/hibmc/Kconfig"
source "drivers/gpu/drm/hisilicon/kirin/Kconfig"
diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile
index e3f6d49..c8155bf 100644
--- a/drivers/gpu/drm/hisilicon/Makefile
+++ b/drivers/gpu/drm/hisilicon/Makefile
@@ -2,4 +2,5 @@
# Makefile for hisilicon drm drivers.
# Please keep this list sorted alphabetically
+obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc/
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
new file mode 100644
index 0000000..380622a
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -0,0 +1,9 @@
+config DRM_HISI_HIBMC
+ tristate "DRM Support for Hisilicon Hibmc"
+ depends on DRM && PCI
+ select DRM_KMS_HELPER
+ select DRM_TTM
+
+ help
+ Choose this option if you have a Hisilicon Hibmc soc chipset.
+ If M is selected the module will be called hibmc-drm.
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
new file mode 100644
index 0000000..47962a0
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -0,0 +1,4 @@
+ccflags-y := -Iinclude/drm
+hibmc-drm-y := hibmc_drm_drv.o
+
+obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
new file mode 100644
index 0000000..6d20580
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -0,0 +1,308 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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/module.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,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+};
+
+static int hibmc_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+ return 0;
+}
+
+static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+}
+
+static struct drm_driver hibmc_driver = {
+ .fops = &hibmc_fops,
+ .name = "hibmc",
+ .date = "20160828",
+ .desc = "hibmc drm driver",
+ .major = 1,
+ .minor = 0,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
+ .enable_vblank = hibmc_enable_vblank,
+ .disable_vblank = hibmc_disable_vblank,
+};
+
+static int hibmc_pm_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int hibmc_pm_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops hibmc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(hibmc_pm_suspend,
+ hibmc_pm_resume)
+};
+
+/*
+ * 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)
+{
+ unsigned int control_value = 0;
+ void __iomem *mmio = priv->mmio;
+ unsigned int input = 1;
+
+ if (power_mode > HIBMC_PW_MODE_CTL_MODE_SLEEP)
+ return;
+
+ if (power_mode == HIBMC_PW_MODE_CTL_MODE_SLEEP)
+ input = 0;
+
+ control_value = readl(mmio + HIBMC_POWER_MODE_CTRL);
+ control_value &= ~(HIBMC_PW_MODE_CTL_MODE_MASK |
+ HIBMC_PW_MODE_CTL_OSC_INPUT_MASK);
+ control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_MODE, power_mode);
+ control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_OSC_INPUT, input);
+ writel(control_value, mmio + HIBMC_POWER_MODE_CTRL);
+}
+
+void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate)
+{
+ unsigned int gate_reg;
+ unsigned int mode;
+ void __iomem *mmio = priv->mmio;
+
+ /* Get current power mode. */
+ mode = (readl(mmio + HIBMC_POWER_MODE_CTRL) &
+ HIBMC_PW_MODE_CTL_MODE_MASK) >> HIBMC_PW_MODE_CTL_MODE_SHIFT;
+
+ switch (mode) {
+ case HIBMC_PW_MODE_CTL_MODE_MODE0:
+ gate_reg = HIBMC_MODE0_GATE;
+ break;
+
+ case HIBMC_PW_MODE_CTL_MODE_MODE1:
+ gate_reg = HIBMC_MODE1_GATE;
+ break;
+
+ default:
+ gate_reg = HIBMC_MODE0_GATE;
+ break;
+ }
+ writel(gate, mmio + gate_reg);
+}
+
+static void hibmc_hw_config(struct hibmc_drm_private *priv)
+{
+ unsigned int reg;
+
+ /* On hardware reset, power mode 0 is default. */
+ hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+ /* Enable display power gate & LOCALMEM power gate*/
+ reg = readl(priv->mmio + HIBMC_CURRENT_GATE);
+ reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+ reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+ reg |= HIBMC_CURR_GATE_DISPLAY(1);
+ reg |= HIBMC_CURR_GATE_LOCALMEM(1);
+
+ hibmc_set_current_gate(priv, reg);
+
+ /*
+ * Reset the memory controller. If the memory controller
+ * is not reset in chip,the system might hang when sw accesses
+ * the memory.The memory should be resetted after
+ * changing the MXCLK.
+ */
+ reg = readl(priv->mmio + HIBMC_MISC_CTRL);
+ reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK;
+ reg |= HIBMC_MSCCTL_LOCALMEM_RESET(0);
+ writel(reg, priv->mmio + HIBMC_MISC_CTRL);
+
+ reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK;
+ reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1);
+
+ writel(reg, priv->mmio + HIBMC_MISC_CTRL);
+}
+
+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;
+
+ ioaddr = pci_resource_start(pdev, 1);
+ iosize = pci_resource_len(pdev, 1);
+ priv->mmio = devm_ioremap_nocache(dev->dev, ioaddr, iosize);
+ if (!priv->mmio) {
+ DRM_ERROR("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;
+}
+
+static int hibmc_hw_init(struct hibmc_drm_private *priv)
+{
+ int ret;
+
+ ret = hibmc_hw_map(priv);
+ if (ret)
+ return ret;
+
+ hibmc_hw_config(priv);
+
+ return 0;
+}
+
+static int hibmc_unload(struct drm_device *dev)
+{
+ return 0;
+}
+
+static int hibmc_load(struct drm_device *dev)
+{
+ struct hibmc_drm_private *priv;
+ 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 0;
+
+err:
+ hibmc_unload(dev);
+ DRM_ERROR("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 drm_device *dev;
+ int ret;
+
+ dev = drm_dev_alloc(&hibmc_driver, &pdev->dev);
+ if (!dev) {
+ DRM_ERROR("failed to allocate drm_device\n");
+ return -ENOMEM;
+ }
+
+ dev->pdev = pdev;
+ pci_set_drvdata(pdev, dev);
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ DRM_ERROR("failed to enable pci device: %d\n", ret);
+ goto err_free;
+ }
+
+ ret = hibmc_load(dev);
+ if (ret) {
+ DRM_ERROR("failed to load hibmc: %d\n", ret);
+ goto err_disable;
+ }
+
+ ret = drm_dev_register(dev, 0);
+ if (ret) {
+ DRM_ERROR("failed to register drv for userspace access: %d\n",
+ ret);
+ goto err_unload;
+ }
+ return 0;
+
+err_unload:
+ hibmc_unload(dev);
+err_disable:
+ pci_disable_device(pdev);
+err_free:
+ drm_dev_unref(dev);
+
+ return ret;
+}
+
+static void hibmc_pci_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ drm_dev_unregister(dev);
+ hibmc_unload(dev);
+ drm_dev_unref(dev);
+}
+
+static struct pci_device_id hibmc_pci_table[] = {
+ {0x19e5, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+static struct pci_driver hibmc_pci_driver = {
+ .name = "hibmc-drm",
+ .id_table = hibmc_pci_table,
+ .probe = hibmc_pci_probe,
+ .remove = hibmc_pci_remove,
+ .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);
+
+MODULE_DEVICE_TABLE(pci, hibmc_pci_table);
+MODULE_AUTHOR("RongrongZou <zourongrong@huawei.com>");
+MODULE_DESCRIPTION("DRM Driver for Hisilicon Hibmc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
new file mode 100644
index 0000000..840cd5a
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -0,0 +1,41 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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.
+ *
+ */
+
+#ifndef HIBMC_DRM_DRV_H
+#define HIBMC_DRM_DRV_H
+
+#include <drm/drmP.h>
+
+struct hibmc_drm_private {
+ /* hw */
+ void __iomem *mmio;
+ void __iomem *fb_map;
+ unsigned long fb_base;
+ unsigned long fb_size;
+
+ /* drm */
+ struct drm_device *dev;
+
+};
+
+void hibmc_set_power_mode(struct hibmc_drm_private *priv,
+ unsigned int power_mode);
+void hibmc_set_current_gate(struct hibmc_drm_private *priv,
+ unsigned int gate);
+
+#endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
new file mode 100644
index 0000000..f7035bf
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
@@ -0,0 +1,196 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ * 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.
+ *
+ */
+
+#ifndef HIBMC_DRM_HW_H
+#define HIBMC_DRM_HW_H
+
+/* register definition */
+#define HIBMC_MISC_CTRL 0x4
+
+#define HIBMC_MSCCTL_LOCALMEM_RESET(x) ((x) << 6)
+#define HIBMC_MSCCTL_LOCALMEM_RESET_MASK 0x40
+
+#define HIBMC_CURRENT_GATE 0x000040
+#define HIBMC_CURR_GATE_DISPLAY(x) ((x) << 2)
+#define HIBMC_CURR_GATE_DISPLAY_MASK 0x4
+
+#define HIBMC_CURR_GATE_LOCALMEM(x) ((x) << 1)
+#define HIBMC_CURR_GATE_LOCALMEM_MASK 0x2
+
+#define HIBMC_MODE0_GATE 0x000044
+#define HIBMC_MODE1_GATE 0x000048
+#define HIBMC_POWER_MODE_CTRL 0x00004C
+
+#define HIBMC_PW_MODE_CTL_OSC_INPUT(x) ((x) << 3)
+#define HIBMC_PW_MODE_CTL_OSC_INPUT_MASK 0x8
+
+#define HIBMC_PW_MODE_CTL_MODE(x) ((x) << 0)
+#define HIBMC_PW_MODE_CTL_MODE_MASK 0x03
+#define HIBMC_PW_MODE_CTL_MODE_SHIFT 0
+
+#define HIBMC_PW_MODE_CTL_MODE_MODE0 0
+#define HIBMC_PW_MODE_CTL_MODE_MODE1 1
+#define HIBMC_PW_MODE_CTL_MODE_SLEEP 2
+
+#define HIBMC_PANEL_PLL_CTRL 0x00005C
+#define HIBMC_CRT_PLL_CTRL 0x000060
+
+#define HIBMC_PLL_CTRL_BYPASS(x) ((x) << 18)
+#define HIBMC_PLL_CTRL_BYPASS_MASK 0x40000
+
+#define HIBMC_PLL_CTRL_POWER(x) ((x) << 17)
+#define HIBMC_PLL_CTRL_POWER_MASK 0x20000
+
+#define HIBMC_PLL_CTRL_INPUT(x) ((x) << 16)
+#define HIBMC_PLL_CTRL_INPUT_MASK 0x10000
+
+#define HIBMC_PLL_CTRL_POD(x) ((x) << 14)
+#define HIBMC_PLL_CTRL_POD_MASK 0xC000
+
+#define HIBMC_PLL_CTRL_OD(x) ((x) << 12)
+#define HIBMC_PLL_CTRL_OD_MASK 0x3000
+
+#define HIBMC_PLL_CTRL_N(x) ((x) << 8)
+#define HIBMC_PLL_CTRL_N_MASK 0xF00
+
+#define HIBMC_PLL_CTRL_M(x) ((x) << 0)
+#define HIBMC_PLL_CTRL_M_MASK 0xFF
+
+#define HIBMC_CRT_DISP_CTL 0x80200
+
+#define HIBMC_CRT_DISP_CTL_CRTSELECT(x) ((x) << 25)
+#define HIBMC_CRT_DISP_CTL_CRTSELECT_MASK 0x2000000
+
+#define HIBMC_CRTSELECT_CRT 1
+
+#define HIBMC_CRT_DISP_CTL_CLOCK_PHASE(x) ((x) << 14)
+#define HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK 0x4000
+
+#define HIBMC_CRT_DISP_CTL_VSYNC_PHASE(x) ((x) << 13)
+#define HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK 0x2000
+
+#define HIBMC_CRT_DISP_CTL_HSYNC_PHASE(x) ((x) << 12)
+#define HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK 0x1000
+
+#define HIBMC_CRT_DISP_CTL_TIMING(x) ((x) << 8)
+#define HIBMC_CRT_DISP_CTL_TIMING_MASK 0x100
+
+#define HIBMC_CRT_DISP_CTL_PLANE(x) ((x) << 2)
+#define HIBMC_CRT_DISP_CTL_PLANE_MASK 4
+
+#define HIBMC_CRT_DISP_CTL_FORMAT(x) ((x) << 0)
+#define HIBMC_CRT_DISP_CTL_FORMAT_MASK 0x03
+
+#define HIBMC_CRT_FB_ADDRESS 0x080204
+
+#define HIBMC_CRT_FB_WIDTH 0x080208
+#define HIBMC_CRT_FB_WIDTH_WIDTH(x) ((x) << 16)
+#define HIBMC_CRT_FB_WIDTH_WIDTH_MASK 0x3FFF0000
+#define HIBMC_CRT_FB_WIDTH_OFFS(x) ((x) << 0)
+#define HIBMC_CRT_FB_WIDTH_OFFS_MASK 0x3FFF
+
+#define HIBMC_CRT_HORZ_TOTAL 0x08020C
+#define HIBMC_CRT_HORZ_TOTAL_TOTAL(x) ((x) << 16)
+#define HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK 0xFFF0000
+
+#define HIBMC_CRT_HORZ_TOTAL_DISP_END(x) ((x) << 0)
+#define HIBMC_CRT_HORZ_TOTAL_DISP_END_MASK 0xFFF
+
+#define HIBMC_CRT_HORZ_SYNC 0x080210
+#define HIBMC_CRT_HORZ_SYNC_WIDTH(x) ((x) << 16)
+#define HIBMC_CRT_HORZ_SYNC_WIDTH_MASK 0xFF0000
+
+#define HIBMC_CRT_HORZ_SYNC_START(x) ((x) << 0)
+#define HIBMC_CRT_HORZ_SYNC_START_MASK 0xFFF
+
+#define HIBMC_CRT_VERT_TOTAL 0x080214
+#define HIBMC_CRT_VERT_TOTAL_TOTAL(x) ((x) << 16)
+#define HIBMC_CRT_VERT_TOTAL_TOTAL_MASK 0x7FFF0000
+
+#define HIBMC_CRT_VERT_TOTAL_DISP_END(x) ((x) << 0)
+#define HIBMC_CRT_VERT_TOTAL_DISP_END_MASK 0x7FF
+
+#define HIBMC_CRT_VERT_SYNC 0x080218
+#define HIBMC_CRT_VERT_SYNC_HEIGHT(x) ((x) << 16)
+#define HIBMC_CRT_VERT_SYNC_HEIGHT_MASK 0x3F0000
+
+#define HIBMC_CRT_VERT_SYNC_START(x) ((x) << 0)
+#define HIBMC_CRT_VERT_SYNC_START_MASK 0x7FF
+
+/* Auto Centering */
+#define HIBMC_CRT_AUTO_CENTERING_TL 0x080280
+#define HIBMC_CRT_AUTO_CENTERING_TL_TOP(x) ((x) << 16)
+#define HIBMC_CRT_AUTO_CENTERING_TL_TOP_MASK 0x7FF0000
+
+#define HIBMC_CRT_AUTO_CENTERING_TL_LEFT(x) ((x) << 0)
+#define HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MASK 0x7FF
+
+#define HIBMC_CRT_AUTO_CENTERING_BR 0x080284
+#define HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(x) ((x) << 16)
+#define HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK 0x7FF0000
+
+#define HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x) ((x) << 0)
+#define HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK 0x7FF
+
+/* register to control panel output */
+#define HIBMC_DISPLAY_CONTROL_HISILE 0x80288
+#define HIBMC_DISPLAY_CONTROL_FPVDDEN(x) ((x) << 0)
+#define HIBMC_DISPLAY_CONTROL_PANELDATE(x) ((x) << 1)
+#define HIBMC_DISPLAY_CONTROL_FPEN(x) ((x) << 2)
+#define HIBMC_DISPLAY_CONTROL_VBIASEN(x) ((x) << 3)
+
+#define HIBMC_RAW_INTERRUPT 0x80290
+#define HIBMC_RAW_INTERRUPT_VBLANK(x) ((x) << 2)
+#define HIBMC_RAW_INTERRUPT_VBLANK_MASK 0x4
+
+#define HIBMC_RAW_INTERRUPT_EN 0x80298
+#define HIBMC_RAW_INTERRUPT_EN_VBLANK(x) ((x) << 2)
+#define HIBMC_RAW_INTERRUPT_EN_VBLANK_MASK 0x4
+
+/* register and values for PLL control */
+#define CRT_PLL1_HS 0x802a8
+#define CRT_PLL1_HS_OUTER_BYPASS(x) ((x) << 30)
+#define CRT_PLL1_HS_INTER_BYPASS(x) ((x) << 29)
+#define CRT_PLL1_HS_POWERON(x) ((x) << 24)
+
+#define CRT_PLL1_HS_25MHZ 0x23d40f02
+#define CRT_PLL1_HS_40MHZ 0x23940801
+#define CRT_PLL1_HS_65MHZ 0x23940d01
+#define CRT_PLL1_HS_78MHZ 0x23540F82
+#define CRT_PLL1_HS_74MHZ 0x23941dc2
+#define CRT_PLL1_HS_80MHZ 0x23941001
+#define CRT_PLL1_HS_80MHZ_1152 0x23540fc2
+#define CRT_PLL1_HS_108MHZ 0x23b41b01
+#define CRT_PLL1_HS_162MHZ 0x23480681
+#define CRT_PLL1_HS_148MHZ 0x23541dc2
+#define CRT_PLL1_HS_193MHZ 0x234807c1
+
+#define CRT_PLL2_HS 0x802ac
+#define CRT_PLL2_HS_25MHZ 0x206B851E
+#define CRT_PLL2_HS_40MHZ 0x30000000
+#define CRT_PLL2_HS_65MHZ 0x40000000
+#define CRT_PLL2_HS_78MHZ 0x50E147AE
+#define CRT_PLL2_HS_74MHZ 0x602B6AE7
+#define CRT_PLL2_HS_80MHZ 0x70000000
+#define CRT_PLL2_HS_108MHZ 0x80000000
+#define CRT_PLL2_HS_162MHZ 0xA0000000
+#define CRT_PLL2_HS_148MHZ 0xB0CCCCCD
+#define CRT_PLL2_HS_193MHZ 0xC0872B02
+
+#define HIBMC_FIELD(field, value) (field(value) & field##_MASK)
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v7 0/7] Add DRM driver for Hisilicon Hibmc
From: Rongrong Zou @ 2016-11-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
This patch set adds a new drm driver for Hisilicon Hibmc. Hibmc is a
BMC SoC with a display controller intergrated, usually it is used on
server for Out-of-band management purpose. In this patch set, we just
support basic function for Hibmc display subsystem. Hibmc display
subsystem is connected to host CPU by PCIe as blow:
+----------+ +----------+
| | PCIe | Hibmc |
|host CPU( |<----->| display |
|arm64,x86)| |subsystem |
+----------+ +----------+
Hardware Detail for Hibmc display subsystem
-----------
The display subsystem of Hibmc is show as bellow:
+----+ +----+ +----+ +--------+
| | | | | | | |
| FB |----->| DE |----->|VDAC|---->|external|
| | | | | | | VGA |
+----+ +----+ +----+ +--------+
-DE(Display Engine) is the display controller.
-VDAC(Video Digital-to-Analog converter) converts the RGB diaital data
stream from DE to VGA analog signals.
Change History
------------
Changes in v7:
-remove hibmc_drm_power.c/hibmc_drm_power.h, move the functions to
hibmc_drm_drv.c.
-remove hibmc_drm_de.h and move the struct defined in head file to
hibmc_drm_de.c.
-plane is initialized inside crtc, not in hibmc_kms_init().
-connector is initialized inside encoder, not in hibmc_kms_init().
-remove plane/crtc/encoder/connector from hibmc_drm_private struct.
-call drm_atomic_helper_suspend/resume in hibmc_pm_suspend/resume.
-remove these empty stubs because caller will do NULL check.
hibmc_plane_atomic_disable
hibmc_crtc_atomic_check
hibmc_encoder_disable
hibmc_encoder_enable
hibmc_encoder_atomic_check
-clean up in all error paths of creating driver-private framebuffer.
Changes in v6:
-remove the embedded framebuffer and use a pointer of hibmc_framebuffer
instead.
-remove the deprecated drm_framebuffer_unregister_private(),
drm_framebuffer_unreference() will be called in hibmc_fbdev_destroy().
-uninstall irq in hibmc_unload().
Changes in v5:
-rebase on v4.9-rc2.
-replace drm_fb_helper_set_suspend with drm_fb_helper_set_suspend_unlocked.
and remove redundant console_lock and console_unlock.
Changes in v4:
-remove unused include files, and include header file when it is needed.
-remove unused FLAG in Kconfig: DRM_GEM_CMA_HELPER/DRM_KMS_CMA_HELPER.
-remove drm_helper_disable_unused_functions, since we use DRIVER_ATOMIC.
Changes in v3:
-enable KMS, in v2, only fbdev is enabled.
-management video memory with ttm.
-add vblank interrupt.
-remove drm_connector_register_all() and drm_connector_unregister_all().
-I have a basic test with igt.
Changes in v2:
-Remove self-defined macros for bit operations.
-Remove unused register.
-Replace those deprecated functions with new version of them.
-use drm_connector_register_all() to register connector after
drm_dev_register().
The patch v2 is at
https://lists.freedesktop.org/archives/dri-devel/2016-May/108661.html
Rongrong Zou (7):
drm/hisilicon/hibmc: Add hisilicon hibmc drm master driver
drm/hisilicon/hibmc: Add video memory management
drm/hisilicon/hibmc: Add support for frame buffer
drm/hisilicon/hibmc: Add support for display engine
drm/hisilicon/hibmc: Add support for VDAC
drm/hisilicon/hibmc: Add support for vblank interrupt
MAINTAINERS: Update HISILICON DRM entries
MAINTAINERS | 1 +
drivers/gpu/drm/hisilicon/Kconfig | 1 +
drivers/gpu/drm/hisilicon/Makefile | 1 +
drivers/gpu/drm/hisilicon/hibmc/Kconfig | 9 +
drivers/gpu/drm/hisilicon/hibmc/Makefile | 4 +
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 477 ++++++++++++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 456 ++++++++++++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 114 +++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 267 +++++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h | 196 ++++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 147 ++++++
drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c | 558 ++++++++++++++++++++++
12 files changed, 2231 insertions(+)
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Kconfig
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Makefile
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
--
1.9.1
^ permalink raw reply
* [PATCH v7 00/16] ACPI IORT ARM SMMU support
From: Tomasz Nowicki @ 2016-11-16 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161109141948.19244-1-lorenzo.pieralisi@arm.com>
On 09.11.2016 15:19, Lorenzo Pieralisi wrote:
> This patch series is v7 of a previous posting:
>
> https://lkml.org/lkml/2016/10/18/506
>
[...]
>
> The ACPI IORT table provides information that allows instantiating
> ARM SMMU devices and carrying out id mappings between components on
> ARM based systems (devices, IOMMUs, interrupt controllers).
>
> http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf
>
> Building on basic IORT support, this patchset enables ARM SMMUs support
> on ACPI systems.
>
> Most of the code is aimed at building the required generic ACPI
> infrastructure to create and enable IOMMU components and to bring
> the IOMMU infrastructure for ACPI on par with DT, which is going to
> make future ARM SMMU components easier to integrate.
>
[...]
>
> This patchset is provided for review/testing purposes here:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git acpi/iort-smmu-v7
>
> Tested on Juno and FVP models for ARM SMMU v1 and v3 probing path.
>
For all series:
Reviewed-by: Tomasz Nowicki <tn@semihalf.com>
Thanks,
Tomasz
^ permalink raw reply
* [PATCH net 1/3] net: phy: realtek: add eee advertisement disable options
From: Andrew Lunn @ 2016-11-16 13:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479290189.17538.25.camel@baylibre.com>
> There two kind of PHYs supporting eee, the one advertising eee by
> default (like realtek) and the one not advertising it (like micrel).
I don't know too much about EEE. So maybe a dumb question. Does the
MAC need to be involved? Or is it just the PHY?
If the MAC needs to be involved, the PHY should not be advertising EEE
unless the MAC asks for it by calling phy_init_eee(). If this is true,
maybe we need to change the realtek driver, and others in that class.
Andrew
^ permalink raw reply
* [PATCH v2] ARM: dts: sun5i: Add touchscreen node to reference-design-tablet.dtsi
From: Hans de Goede @ 2016-11-16 13:15 UTC (permalink / raw)
To: linux-arm-kernel
Just like on sun8i all sun5i tablets use the same interrupt and power
gpios for their touchscreens. I've checked all known a13 fex files and
only the UTOO P66 uses a different gpio for the interrupt.
Add a touchscreen node to sun5i-reference-design-tablet.dtsi, which
fills in the necessary gpios to avoid duplication in the tablet dts files,
just like we do in sun8i-reference-design-tablet.dtsi.
This will make future patches adding touchscreen nodes to a13 tablets
simpler.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
-Use generic pin mux properties "pins", "function", "drive-strength" and
"bias-disable"
---
arch/arm/boot/dts/sun5i-a13-utoo-p66.dts | 38 ++++++++--------------
.../boot/dts/sun5i-reference-design-tablet.dtsi | 25 ++++++++++++++
2 files changed, 39 insertions(+), 24 deletions(-)
diff --git a/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts b/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
index a8b0bcc..3d7ff10 100644
--- a/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
+++ b/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
@@ -83,22 +83,6 @@
allwinner,pins = "PG3";
};
-&i2c1 {
- icn8318: touchscreen at 40 {
- compatible = "chipone,icn8318";
- reg = <0x40>;
- interrupt-parent = <&pio>;
- interrupts = <6 9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
- pinctrl-names = "default";
- pinctrl-0 = <&ts_wake_pin_p66>;
- wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
- touchscreen-size-x = <800>;
- touchscreen-size-y = <480>;
- touchscreen-inverted-x;
- touchscreen-swapped-x-y;
- };
-};
-
&mmc2 {
pinctrl-names = "default";
pinctrl-0 = <&mmc2_pins_a>;
@@ -121,20 +105,26 @@
allwinner,drive = <SUN4I_PINCTRL_10_MA>;
allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
};
-
- ts_wake_pin_p66: ts_wake_pin at 0 {
- allwinner,pins = "PB3";
- allwinner,function = "gpio_out";
- allwinner,drive = <SUN4I_PINCTRL_10_MA>;
- allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
- };
-
};
®_usb0_vbus {
gpio = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */
};
+&touchscreen {
+ compatible = "chipone,icn8318";
+ reg = <0x40>;
+ /* The P66 uses a different EINT then the reference design */
+ interrupts = <6 9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
+ /* The icn8318 binding expects wake-gpios instead of power-gpios */
+ wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
+ touchscreen-size-x = <800>;
+ touchscreen-size-y = <480>;
+ touchscreen-inverted-x;
+ touchscreen-swapped-x-y;
+ status = "okay";
+};
+
&uart1 {
/* The P66 uses the uart pins as gpios */
status = "disabled";
diff --git a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
index 20cc940..82f87cd 100644
--- a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
+++ b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
@@ -41,6 +41,7 @@
*/
#include "sunxi-reference-design-tablet.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/pwm/pwm.h>
/ {
@@ -84,6 +85,23 @@
};
&i2c1 {
+ /*
+ * The gsl1680 is rated at 400KHz and it will not work reliable at
+ * 100KHz, this has been confirmed on multiple different q8 tablets.
+ * All other devices on this bus are also rated for 400KHz.
+ */
+ clock-frequency = <400000>;
+
+ touchscreen: touchscreen {
+ interrupt-parent = <&pio>;
+ interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>; /* EINT11 (PG11) */
+ pinctrl-names = "default";
+ pinctrl-0 = <&ts_power_pin>;
+ power-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
+ /* Tablet dts must provide reg and compatible */
+ status = "disabled";
+ };
+
pcf8563: rtc at 51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
@@ -125,6 +143,13 @@
allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
};
+ ts_power_pin: ts_power_pin {
+ pins = "PB3";
+ function = "gpio_out";
+ drive-strength = <10>;
+ bias-disable;
+ };
+
usb0_vbus_detect_pin: usb0_vbus_detect_pin at 0 {
allwinner,pins = "PG1";
allwinner,function = "gpio_in";
--
2.9.3
^ permalink raw reply related
* [PATCH 0/8] DMA: s3c64xx: Conversion to the new channel request API
From: Sylwester Nawrocki @ 2016-11-16 12:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161116032620.GV3000@localhost>
On 11/16/2016 04:26 AM, Vinod Koul wrote:
> On Thu, Nov 10, 2016 at 04:17:48PM +0100, Sylwester Nawrocki wrote:
>> This patch series aims to convert the s3c64xx platform to use
>> the new DMA channel request API, i.e. this is only meaningful
>> for non-dt systems using s3c64xx SoCs.
>>
>> Presumably the first 2 or 4 patches in this series could be queued
>> for v4.10-rc1 and the remaining patches could be left for subsequent
>> release, to avoid non-trivial conflict with patches already applied
>> in the ASoC tree.
>
> I am fine with dma patch (expect the subsystem tag) and others except arm
> ones have acks, so I think we can merge this for v4.10-rc1. I cna create a
> immutable tag and people can merge into their tree in case they have
> dependencies.
>
> Btw need acks on ARM patches before I can apply
Great, then we an Ack for these 2 patches:
ARM: s3c64xx: Add DMA slave maps for PL080 devices
ARM: s3c64xx: Drop unused DMA fields from struct s3c64xx_spi_csinfo
--
Thanks,
Sylwester
^ permalink raw reply
* [PATCH v6] drm: tilcdc: implement palette loading for rev1
From: Bartosz Golaszewski @ 2016-11-16 12:57 UTC (permalink / raw)
To: linux-arm-kernel
Revision 1 of the IP doesn't work if we don't load the palette (even
if it's not used, which is the case for the RGB565 format).
Add a function called from tilcdc_crtc_enable() which performs all
required actions if we're dealing with a rev1 chip.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
v1 -> v2:
- only allocate dma memory for revision 1
v2 -> v3:
- use devres managed API for dma memory allocation
v3 -> v4:
- reinit the palette completion in tilcdc_crtc_disable()
v4 -> v5:
[nothing regarding this patch]
v5 -> v6:
- minor coding style fixes
drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 88 +++++++++++++++++++++++++++++++++++-
1 file changed, 87 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index f4ee9c2..dfe3dd0 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -21,11 +21,15 @@
#include <drm/drm_flip_work.h>
#include <drm/drm_plane_helper.h>
#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
-#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000
+#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000
+#define TILCDC_REV1_PALETTE_SIZE 32
+#define TILCDC_REV1_PALETTE_FIRST_ENTRY 0x4000
struct tilcdc_crtc {
struct drm_crtc base;
@@ -53,6 +57,10 @@ struct tilcdc_crtc {
int sync_lost_count;
bool frame_intact;
+
+ dma_addr_t palette_dma_handle;
+ void *palette_base;
+ struct completion palette_loaded;
};
#define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base)
@@ -98,6 +106,55 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
tilcdc_crtc->curr_fb = fb;
}
+/*
+ * The driver currently only supports the RGB565 format for revision 1. For
+ * 16 bits-per-pixel the palette block is bypassed, but the first 32 bytes of
+ * the framebuffer are still considered palette. The first 16-bit entry must
+ * be 0x4000 while all other entries must be zeroed.
+ */
+static void tilcdc_crtc_load_palette(struct drm_crtc *crtc)
+{
+ u32 dma_fb_base, dma_fb_ceiling, raster_ctl;
+ struct tilcdc_crtc *tilcdc_crtc;
+ struct drm_device *dev;
+ u16 *first_entry;
+
+ dev = crtc->dev;
+ tilcdc_crtc = to_tilcdc_crtc(crtc);
+ first_entry = tilcdc_crtc->palette_base;
+
+ *first_entry = TILCDC_REV1_PALETTE_FIRST_ENTRY;
+
+ dma_fb_base = tilcdc_read(dev, LCDC_DMA_FB_BASE_ADDR_0_REG);
+ dma_fb_ceiling = tilcdc_read(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG);
+ raster_ctl = tilcdc_read(dev, LCDC_RASTER_CTRL_REG);
+
+ /* Tell the LCDC where the palette is located. */
+ tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG,
+ tilcdc_crtc->palette_dma_handle);
+ tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG,
+ (u32)tilcdc_crtc->palette_dma_handle +
+ TILCDC_REV1_PALETTE_SIZE - 1);
+
+ /* Load it. */
+ tilcdc_clear(dev, LCDC_RASTER_CTRL_REG,
+ LCDC_PALETTE_LOAD_MODE(DATA_ONLY));
+ tilcdc_set(dev, LCDC_RASTER_CTRL_REG,
+ LCDC_PALETTE_LOAD_MODE(PALETTE_ONLY));
+
+ /* Enable the LCDC and wait for palette to be loaded. */
+ tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA);
+ tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
+
+ wait_for_completion(&tilcdc_crtc->palette_loaded);
+
+ /* Restore the registers. */
+ tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
+ tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_fb_base);
+ tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, dma_fb_ceiling);
+ tilcdc_write(dev, LCDC_RASTER_CTRL_REG, raster_ctl);
+}
+
static void tilcdc_crtc_enable_irqs(struct drm_device *dev)
{
struct tilcdc_drm_private *priv = dev->dev_private;
@@ -154,6 +211,7 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
+ struct tilcdc_drm_private *priv = dev->dev_private;
WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
@@ -164,6 +222,9 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
reset(crtc);
+ if (priv->rev == 1 && !completion_done(&tilcdc_crtc->palette_loaded))
+ tilcdc_crtc_load_palette(crtc);
+
tilcdc_crtc_enable_irqs(dev);
tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE);
@@ -202,6 +263,13 @@ void tilcdc_crtc_disable(struct drm_crtc *crtc)
__func__);
}
+ /*
+ * LCDC will not retain the palette when reset. Make sure it gets
+ * reloaded on tilcdc_crtc_enable().
+ */
+ if (priv->rev == 1)
+ reinit_completion(&tilcdc_crtc->palette_loaded);
+
drm_crtc_vblank_off(crtc);
tilcdc_crtc_disable_irqs(dev);
@@ -804,6 +872,14 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)
dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow",
__func__, stat);
+ if (priv->rev == 1) {
+ if (stat & LCDC_PL_LOAD_DONE) {
+ complete(&tilcdc_crtc->palette_loaded);
+ tilcdc_clear(dev,
+ LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA);
+ }
+ }
+
/* For revision 2 only */
if (priv->rev == 2) {
if (stat & LCDC_FRAME_DONE) {
@@ -865,6 +941,16 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)
return NULL;
}
+ if (priv->rev == 1) {
+ init_completion(&tilcdc_crtc->palette_loaded);
+ tilcdc_crtc->palette_base = dmam_alloc_coherent(dev->dev,
+ TILCDC_REV1_PALETTE_SIZE,
+ &tilcdc_crtc->palette_dma_handle,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!tilcdc_crtc->palette_base)
+ return ERR_PTR(-ENOMEM);
+ }
+
crtc = &tilcdc_crtc->base;
ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary);
--
2.9.3
^ permalink raw reply related
* [PATCH] cpufreq: dt: Add support for r8a7743 and r8a7745
From: Simon Horman @ 2016-11-16 12:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479290751-9705-1-git-send-email-geert+renesas@glider.be>
On Wed, Nov 16, 2016 at 11:05:51AM +0100, Geert Uytterhoeven wrote:
> Add the compatible strings for supporting the generic cpufreq driver on
> the Renesas RZ/G1M (r8a7743) and RZ/G1E (r8a7745) SoCs.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
> Support for RZ/G1M and RZ/G1E is expected to land in v4.10.
Acked-by: Simon Horman <horms+renesas@verge.net.au>
^ permalink raw reply
* [PATCH 1/2] ARM: dts: rockchip: add the sdmmc pinctrl for rk1108
From: Heiko Stuebner @ 2016-11-16 12:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479024799-29198-1-git-send-email-jacob-chen@iotwrt.com>
Am Sonntag, 13. November 2016, 16:13:18 CET schrieb Jacob Chen:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> ---
> arch/arm/boot/dts/rk1108.dtsi | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/arch/arm/boot/dts/rk1108.dtsi b/arch/arm/boot/dts/rk1108.dtsi
> index 9dccfea..6a06ad7 100644
> --- a/arch/arm/boot/dts/rk1108.dtsi
> +++ b/arch/arm/boot/dts/rk1108.dtsi
> @@ -321,6 +321,31 @@
> input-enable;
> };
>
> + sdmmc {
I've fixed the intendation here (needed tabs instead of spaces) and moved the
block to its alphabetically correct position between i2c and uart nodes and
applied the result to my dts32 branch.
Thanks
Heiko
^ permalink raw reply
* [PATCH] ARM64: dma-mapping: preallocate DMA-debug hash tables in core_initcall
From: Catalin Marinas @ 2016-11-16 12:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479288013-30945-1-git-send-email-m.szyprowski@samsung.com>
On Wed, Nov 16, 2016 at 10:20:13AM +0100, Marek Szyprowski wrote:
> fs_initcall is definitely too late to initialize DMA-debug hash tables,
> because some drivers might get probed and use DMA mapping framework
> already in core_initcall. Late initialization of DMA-debug results in
> false warning about accessing memory, that was not allocated. This issue
> has been observed on ARM 32bit, but the same driver can be used also on
> ARM64.
>
> This patch moves initialization of DMA-debug to core_initcall. This is
> safe from the initialization perspective. dma_debug_do_init() internally
> calls debugfs functions and debugfs also gets initialised at
> core_initcall(), and that is earlier than arch code in the link order,
> so it will get initialized just before the DMA-debug.
Do we really want to rely on the link order within an initcall level?
What guarantees this?
I hope someone sorts out the deferred probe or some other dependency
detection mechanism to address this issue. But in the meantime I
wouldn't merge a patch which relies on just the link order.
--
Catalin
^ permalink raw reply
* [PATCH 2/2] ARM: dts: rockchip: enable sdmmc for rk1108-evb
From: Heiko Stuebner @ 2016-11-16 12:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479024799-29198-2-git-send-email-jacob-chen@iotwrt.com>
Am Sonntag, 13. November 2016, 16:13:19 CET schrieb Jacob Chen:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This patch add sdmmc support for rk1108-evb, now I can load the rootfs
> from sdmmc.
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> ---
> arch/arm/boot/dts/rk1108-evb.dts | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/arch/arm/boot/dts/rk1108-evb.dts
> b/arch/arm/boot/dts/rk1108-evb.dts index 3956cff..cea26e5 100644
> --- a/arch/arm/boot/dts/rk1108-evb.dts
> +++ b/arch/arm/boot/dts/rk1108-evb.dts
> @@ -56,6 +56,18 @@
> };
> };
>
> +&sdmmc {
> + bus-width = <4>;
> + cap-mmc-highspeed;
> + cap-sd-highspeed;
> + card-detect-delay = <200>;
> + disable-wp;
> + num-slots = <1>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
> + status = "okay";
> +};
> +
> &uart0 {
> status = "okay";
> };
> @@ -67,3 +79,12 @@
> &uart2 {
> status = "okay";
> };
> +
> +&pinctrl {
> +
> + sdmmc {
> + sdmmc_pwr: sdmmc-pwr {
> + rockchip,pins = <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>;
> + };
the above is unsued, and in general please use a fixed regulator as vmmc, as
all other Rockchip boards do already, so that the mmc core can handle it
itself.
Thanks
Heiko
^ permalink raw reply
* [PATCH v2 10/10] ARM: dts: rockchip: add rockchip RK1108 Evaluation board
From: Heiko Stuebner @ 2016-11-16 11:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479125863-24646-1-git-send-email-andy.yan@rock-chips.com>
Am Montag, 14. November 2016, 20:17:43 CET schrieb Andy Yan:
> RK1108 EVB is designed by Rockchip for CVR field.
> This patch add basic support for it, which can boot with
> initramfs into shell.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Acked-by: Rob Herring <robh@kernel.org>
>
> ---
>
> Changes in v2:
> - move the board in the rockchip.txt to the block of Rockchip boards
>
> Documentation/devicetree/bindings/arm/rockchip.txt | 5 +-
> arch/arm/boot/dts/Makefile | 1 +
> arch/arm/boot/dts/rk1108-evb.dts | 69
> ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-)
> create mode 100644 arch/arm/boot/dts/rk1108-evb.dts
>
> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt
> b/Documentation/devicetree/bindings/arm/rockchip.txt index 10b92b5..e658b62
> 100644
> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> @@ -1,6 +1,5 @@
> Rockchip platforms device tree bindings
> ---------------------------------------
> -
> - Kylin RK3036 board:
> Required root node properties:
> - compatible = "rockchip,kylin-rk3036", "rockchip,rk3036";
dropped this unrelated change
> @@ -111,6 +110,10 @@ Rockchip platforms device tree bindings
> Required root node properties:
> - compatible = "rockchip,px5-evb", "rockchip,px5", "rockchip,rk3368";
>
> +- Rockchip RK1108 Evaluation board
> + Required root node properties:
> + - compatible = "rockchip,rk1108-evb", "rockchip,rk1108";
> +
> - Rockchip RK3368 evb:
> Required root node properties:
> - compatible = "rockchip,rk3368-evb-act8846", "rockchip,rk3368";
binding moved to a separate patch and applied to my dts64 to prevent conflicts
with px5 addition.
And the actual board dts of course applied to my dts32 branch.
Thanks
Heiko
^ permalink raw reply
* [PATCH] ARM64: dma-mapping: preallocate DMA-debug hash tables in core_initcall
From: Robin Murphy @ 2016-11-16 11:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479288013-30945-1-git-send-email-m.szyprowski@samsung.com>
On 16/11/16 09:20, Marek Szyprowski wrote:
> fs_initcall is definitely too late to initialize DMA-debug hash tables,
> because some drivers might get probed and use DMA mapping framework
> already in core_initcall. Late initialization of DMA-debug results in
> false warning about accessing memory, that was not allocated. This issue
> has been observed on ARM 32bit, but the same driver can be used also on
> ARM64.
>
> This patch moves initialization of DMA-debug to core_initcall. This is
> safe from the initialization perspective. dma_debug_do_init() internally
> calls debugfs functions and debugfs also gets initialised at
> core_initcall(), and that is earlier than arch code in the link order,
> so it will get initialized just before the DMA-debug.
>
> Reported-by: Seung-Woo Kim <sw0312.kim@samsung.com>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Acked-by: Robin Murphy <robin.murphy@arm.com>
Cheers Marek - as it happens, the ARM SMMU drivers (and presumably the
Mediatek one) do hit this on arm64 since I added the DMA API support to
the io-pgtable code, I've just been quietly ignoring it in the hope we'd
get the probe-deferral stuff done before anyone else noticed.
Robin.
> ---
> For more details on this issue, see the patch for ARM 32bit arch:
> https://www.spinics.net/lists/arm-kernel/msg542721.html
> https://www.spinics.net/lists/arm-kernel/msg542782.html
> ---
> arch/arm64/mm/dma-mapping.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
> index 3f74d0d..8653426 100644
> --- a/arch/arm64/mm/dma-mapping.c
> +++ b/arch/arm64/mm/dma-mapping.c
> @@ -538,7 +538,7 @@ static int __init dma_debug_do_init(void)
> dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
> return 0;
> }
> -fs_initcall(dma_debug_do_init);
> +core_initcall(dma_debug_do_init);
>
>
> #ifdef CONFIG_IOMMU_DMA
>
^ permalink raw reply
* [PATCH v2] input: touchscreen: silead: Add regulator support
From: Hans de Goede @ 2016-11-16 11:55 UTC (permalink / raw)
To: linux-arm-kernel
On some tablets the touchscreen controller is powered by separate
regulators, add support for this.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v2:
-Use devm_regulator_bulk_get() and friends
-Use devm_add_action_or_reset() to disable the regulator
---
.../bindings/input/touchscreen/silead_gsl1680.txt | 2 ++
drivers/input/touchscreen/silead.c | 29 ++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt b/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
index e844c3f..b726823 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
@@ -22,6 +22,8 @@ Optional properties:
- touchscreen-inverted-y : See touchscreen.txt
- touchscreen-swapped-x-y : See touchscreen.txt
- silead,max-fingers : maximum number of fingers the touchscreen can detect
+- vddio-supply : regulator phandle for controller VDDIO
+- avdd-supply : regulator phandle for controller AVDD
Example:
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
index f502c84..404830a 100644
--- a/drivers/input/touchscreen/silead.c
+++ b/drivers/input/touchscreen/silead.c
@@ -29,6 +29,7 @@
#include <linux/input/touchscreen.h>
#include <linux/pm.h>
#include <linux/irq.h>
+#include <linux/regulator/consumer.h>
#include <asm/unaligned.h>
@@ -73,6 +74,7 @@ struct silead_ts_data {
struct i2c_client *client;
struct gpio_desc *gpio_power;
struct input_dev *input;
+ struct regulator_bulk_data regulators[2];
char fw_name[64];
struct touchscreen_properties prop;
u32 max_fingers;
@@ -433,6 +435,13 @@ static int silead_ts_set_default_fw_name(struct silead_ts_data *data,
}
#endif
+static void silead_disable_regulator(void *arg)
+{
+ struct silead_ts_data *data = arg;
+
+ regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
+}
+
static int silead_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -465,6 +474,26 @@ static int silead_ts_probe(struct i2c_client *client,
if (client->irq <= 0)
return -ENODEV;
+ data->regulators[0].supply = "vddio";
+ data->regulators[1].supply = "avdd";
+ error = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
+ data->regulators);
+ if (error)
+ return error;
+
+ /*
+ * Enable regulators at probe and disable them at remove, we need
+ * to keep the chip powered otherwise it forgets its firmware.
+ */
+ error = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
+ data->regulators);
+ if (error)
+ return error;
+
+ error = devm_add_action_or_reset(dev, silead_disable_regulator, data);
+ if (error)
+ return error;
+
/* Power GPIO pin */
data->gpio_power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
if (IS_ERR(data->gpio_power)) {
--
2.9.3
^ permalink raw reply related
* [PATCH/RESEND] recordmcount: arm: Implement make_nop
From: Ard Biesheuvel @ 2016-11-16 11:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161115235331.GE25626@codeaurora.org>
On 15 November 2016 at 23:53, Stephen Boyd <sboyd@codeaurora.org> wrote:
> On 11/15, Ard Biesheuvel wrote:
>> On 15 November 2016 at 19:18, Stephen Boyd <sboyd@codeaurora.org> wrote:
>> > On 11/15, Ard Biesheuvel wrote:
>> >> On 19 October 2016 at 00:42, Stephen Boyd <sboyd@codeaurora.org> wrote:
>> >> >
>> >> > +static unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
>> >> > +static unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
>> >>
>> >> Shouldn't you be taking the difference between BE8 and BE32 into
>> >> account here? IIRC, BE8 uses little endian encoding for instructions.
>> >
>> > I admit I haven't tested on a pre-armv6 CPU so I haven't come
>> > across the case of a BE32 CPU. But from what I can tell that
>> > doesn't matter.
>> >
>> > According to scripts/Makefile.build, cmd_record_mcount only runs
>> > the recordmcount program if CONFIG_FTRACE_MCOUNT_RECORD=y. That
>> > config is defined as:
>> >
>> > config FTRACE_MCOUNT_RECORD
>> > def_bool y
>> > depends on DYNAMIC_FTRACE
>> > depends on HAVE_FTRACE_MCOUNT_RECORD
>> >
>> >
>> > And in arch/arm/Kconfig we see that DYNAMIC_FTRACE is selected:
>> >
>> > select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) && !CPU_ENDIAN_BE32 && MMU
>> >
>> > which means that FTRACE_MCOUNT_RECORD can't be set when
>> > CPU_ENDIAN_BE32 is set.
>> >
>> > Do you agree that BE32 is not a concern here?
>> >
>>
>> Yes. But that implies then that you should not be using big-endian
>> instruction encodings at all, and simply use the _le variants for both
>> LE and BE8
>
> Ok. I understand what you're getting at now.
>
> I believe the linker is the one that does the instruction endian
> swap to little endian. So everything is built as big-endian data
> and instructions in the assembler phase and then when the linker
> runs to generate the final vmlinux elf file it does the swaps to
> make instructions little endian. recordmcount runs on the object
> files and not the vmlinux file.
>
Very interesting, I did not know that.
> For example, the do_undefinstr() function in
> arch/arm/kernel/traps.c is one place we nop out. On an le host
> and an le build without this patch I see:
>
> (This is all ARM, not thumb)
>
> 00000000 <do_undefinstr>:
> 0: e1a0c00d mov ip, sp
> 4: e92dd9f0 push {r4, r5, r6, r7, r8, fp, ip, lr, pc}
> 8: e24cb004 sub fp, ip, #4
> c: e24dd08c sub sp, sp, #140 ; 0x8c
> 10: e52de004 push {lr} ; (str lr, [sp, #-4]!)
> 14: ebfffffe bl 0 <__gnu_mcount_nc>
>
> After this patch on an le host and le build I see:
>
> 00000000 <do_undefinstr>:
> 0: e1a0c00d mov ip, sp
> 4: e92dd9f0 push {r4, r5, r6, r7, r8, fp, ip, lr, pc}
> 8: e24cb004 sub fp, ip, #4
> c: e24dd08c sub sp, sp, #140 ; 0x8c
> 10: e1a00000 nop ; (mov r0, r0)
> 14: e1a00000 nop ; (mov r0, r0)
>
> So far so good. Similarly, with this patch and an le host and be
> build I see:
>
> 00000000 <do_undefinstr>:
> 0: e1a0c00d mov ip, sp
> 4: e92dd9f0 push {r4, r5, r6, r7, r8, fp, ip, lr, pc}
> 8: e24cb004 sub fp, ip, #4
> c: e24dd08c sub sp, sp, #140 ; 0x8c
> 10: e1a00000 nop ; (mov r0, r0)
> 14: e1a00000 nop ; (mov r0, r0)
>
> but with *_le instead of *_be used a be build I see:
>
> 00000000 <do_undefinstr>:
> 0: e1a0c00d mov ip, sp
> 4: e92dd9f0 push {r4, r5, r6, r7, r8, fp, ip, lr, pc}
> 8: e24cb004 sub fp, ip, #4
> c: e24dd08c sub sp, sp, #140 ; 0x8c
> 10: 0000a0e1 andeq sl, r0, r1, ror #1
> 14: 0000a0e1 andeq sl, r0, r1, ror #1
>
> I confirmed this by looking at the hexdump of the .exception.text
> section for the traps.o object file and the .text section of the
> vmlinux file. Basically objcopy the .exception.text of traps.o to
> get the first few instructions of the do_undefinstr() function:
>
> $ hexdump -C traps.o
> 00000000 e1 a0 c0 0d e9 2d d9 f0 e2 4c b0 04 e2 4d d0 8c
>
> And then objcopy the .text section in vmlinux and seek to the
> same function offset (there are a bunch of zeroes in front of it
> for padding):
>
> $ hexdump -C vmlinux
> ...
> 00001000 0d c0 a0 e1 f0 d9 2d e9 04 b0 4c e2 8c d0 4d e2
>
> As can be seen everything is swapped from the original object
> file in big-endian to be in little endian.
>
> Does that allay your concerns?
>
Yes, it does. Thanks
^ permalink raw reply
* [PATCH v2] mxs-auart: count FIFO overrun errors
From: Fabio Estevam @ 2016-11-16 11:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161116113745.12026-1-weo@reccoware.de>
On Wed, Nov 16, 2016 at 9:37 AM, Wolfgang Ocker <weo@reccoware.de> wrote:
> The mxs-auart driver does not count FIFO overrun errors. These errors never
> appear in /proc/tty/driver/ttyAPP. This is because the OERR status bit is
> masked by read_status_mask in the rx interrupt function, but the
> AUART_STAT_OERR bit is never set in read_status_mask. The patch enables the
> counting of overrun errors.
>
> Signed-off-by: Wolfgang Ocker <weo@reccoware.de>
Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
^ permalink raw reply
* [PATCH v8 0/7] arm/arm64: vgic: Implement API for vGICv3 live migration
From: Christoffer Dall @ 2016-11-16 11:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478258013-6669-1-git-send-email-vijay.kilari@gmail.com>
On Fri, Nov 04, 2016 at 04:43:26PM +0530, vijay.kilari at gmail.com wrote:
> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
>
> This patchset adds API for saving and restoring
> of VGICv3 registers to support live migration with new vgic feature.
> This API definition is as per version of VGICv3 specification
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/445611.html
>
> The patch 3 & 4 are picked from the Pavel's previous implementation.
> http://www.spinics.net/lists/kvm/msg122040.html
Do we have QEMU/kvmtool patches somewhere at this point so that I can
test this?
Thanks,
-Christoffer
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox