Merge tag 'drm-misc-next-2019-08-23' of git://anongit.freedesktop.org/drm/drm-misc...
authorDave Airlie <airlied@redhat.com>
Tue, 27 Aug 2019 07:10:30 +0000 (17:10 +1000)
committerDave Airlie <airlied@redhat.com>
Tue, 27 Aug 2019 07:21:04 +0000 (17:21 +1000)
drm-misc-next for 5.4:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - dma-buf: dma-fence selftests

Driver Changes:
  - kirin: Various cleanups and reworks
  - komeda: Add support for DT memory-regions
  - meson: Rely on the compatible to detect vpu features
  - omap: Implement alpha and pixel blend mode properties
  - panfrost: Implement per-fd address spaces, various fixes
  - rockchip: DSI DT binding rework
  - fbdev: Various cleanups

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190823083509.c7mduqdqjnxc7ubb@flea
57 files changed:
Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
drivers/dma-buf/Kconfig
drivers/dma-buf/Makefile
drivers/dma-buf/selftest.c [new file with mode: 0644]
drivers/dma-buf/selftest.h [new file with mode: 0644]
drivers/dma-buf/selftests.h [new file with mode: 0644]
drivers/dma-buf/st-dma-fence.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_dev.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_kms_helper_common.c
drivers/gpu/drm/hisilicon/kirin/Kconfig
drivers/gpu/drm/hisilicon/kirin/Makefile
drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
drivers/gpu/drm/i915/Kconfig.debug
drivers/gpu/drm/meson/meson_crtc.c
drivers/gpu/drm/meson/meson_drv.c
drivers/gpu/drm/meson/meson_drv.h
drivers/gpu/drm/meson/meson_dw_hdmi.c
drivers/gpu/drm/meson/meson_overlay.c
drivers/gpu/drm/meson/meson_plane.c
drivers/gpu/drm/meson/meson_vclk.c
drivers/gpu/drm/meson/meson_venc.c
drivers/gpu/drm/meson/meson_venc_cvbs.c
drivers/gpu/drm/meson/meson_viu.c
drivers/gpu/drm/meson/meson_vpp.c
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_bo.h
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nouveau_prime.c
drivers/gpu/drm/omapdrm/dss/dss.c
drivers/gpu/drm/omapdrm/omap_plane.c
drivers/gpu/drm/panfrost/TODO
drivers/gpu/drm/panfrost/panfrost_devfreq.c
drivers/gpu/drm/panfrost/panfrost_devfreq.h
drivers/gpu/drm/panfrost/panfrost_device.c
drivers/gpu/drm/panfrost/panfrost_device.h
drivers/gpu/drm/panfrost/panfrost_drv.c
drivers/gpu/drm/panfrost/panfrost_gem.c
drivers/gpu/drm/panfrost/panfrost_gem.h
drivers/gpu/drm/panfrost/panfrost_job.c
drivers/gpu/drm/panfrost/panfrost_mmu.c
drivers/gpu/drm/panfrost/panfrost_mmu.h
drivers/video/fbdev/aty/aty128fb.c
drivers/video/fbdev/aty/atyfb_base.c
drivers/video/fbdev/aty/radeon_base.c
drivers/video/fbdev/efifb.c
drivers/video/fbdev/mmp/core.c
drivers/video/fbdev/pvr2fb.c
drivers/video/fbdev/sh_mobile_lcdcfb.c
drivers/video/fbdev/sm712fb.c
drivers/video/fbdev/udlfb.c
drivers/video/fbdev/via/via-core.c

index 6bb59ab..ce4c1fc 100644 (file)
@@ -14,6 +14,8 @@ Required properties:
 - rockchip,grf: this soc should set GRF regs to mux vopl/vopb.
 - ports: contain a port node with endpoint definitions as defined in [2].
   For vopb,set the reg = <0> and set the reg = <1> for vopl.
+- video port 0 for the VOP input, the remote endpoint maybe vopb or vopl
+- video port 1 for either a panel or subsequent encoder
 
 Optional properties:
 - power-domains: a phandle to mipi dsi power domain node.
@@ -40,11 +42,12 @@ Example:
                ports {
                        #address-cells = <1>;
                        #size-cells = <0>;
-                       reg = <1>;
 
-                       mipi_in: port {
+                       mipi_in: port@0 {
+                               reg = <0>;
                                #address-cells = <1>;
                                #size-cells = <0>;
+
                                mipi_in_vopb: endpoint@0 {
                                        reg = <0>;
                                        remote-endpoint = <&vopb_out_mipi>;
@@ -54,6 +57,16 @@ Example:
                                        remote-endpoint = <&vopl_out_mipi>;
                                };
                        };
+
+                       mipi_out: port@1 {
+                               reg = <1>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               mipi_out_panel: endpoint {
+                                       remote-endpoint = <&panel_in_mipi>;
+                               };
+                       };
                };
 
                panel {
@@ -64,5 +77,11 @@ Example:
                        pinctrl-names = "default";
                        pinctrl-0 = <&lcd_en>;
                        backlight = <&backlight>;
+
+                       port {
+                               panel_in_mipi: endpoint {
+                                       remote-endpoint = <&mipi_out_panel>;
+                               };
+                       };
                };
        };
index b6a9c2f..a23b675 100644 (file)
@@ -39,4 +39,9 @@ config UDMABUF
          A driver to let userspace turn memfd regions into dma-bufs.
          Qemu can use this to create host dmabufs for guest framebuffers.
 
+config DMABUF_SELFTESTS
+       tristate "Selftests for the dma-buf interfaces"
+       default n
+       depends on DMA_SHARED_BUFFER
+
 endmenu
index dcfb01e..03479da 100644 (file)
@@ -4,3 +4,9 @@ obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
 obj-$(CONFIG_SYNC_FILE)                += sync_file.o
 obj-$(CONFIG_SW_SYNC)          += sw_sync.o sync_debug.o
 obj-$(CONFIG_UDMABUF)          += udmabuf.o
+
+dmabuf_selftests-y := \
+       selftest.o \
+       st-dma-fence.o
+
+obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
diff --git a/drivers/dma-buf/selftest.c b/drivers/dma-buf/selftest.c
new file mode 100644 (file)
index 0000000..c60b694
--- /dev/null
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * Copyright Â© 2019 Intel Corporation
+ */
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+
+#include "selftest.h"
+
+enum {
+#define selftest(n, func) __idx_##n,
+#include "selftests.h"
+#undef selftest
+};
+
+#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f },
+static struct selftest {
+       bool enabled;
+       const char *name;
+       int (*func)(void);
+} selftests[] = {
+#include "selftests.h"
+};
+#undef selftest
+
+/* Embed the line number into the parameter name so that we can order tests */
+#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n))
+#define selftest_0(n, func, id) \
+module_param_named(id, selftests[__idx_##n].enabled, bool, 0400);
+#define selftest(n, func) selftest_0(n, func, param(n))
+#include "selftests.h"
+#undef selftest
+
+int __sanitycheck__(void)
+{
+       pr_debug("Hello World!\n");
+       return 0;
+}
+
+static char *__st_filter;
+
+static bool apply_subtest_filter(const char *caller, const char *name)
+{
+       char *filter, *sep, *tok;
+       bool result = true;
+
+       filter = kstrdup(__st_filter, GFP_KERNEL);
+       for (sep = filter; (tok = strsep(&sep, ","));) {
+               bool allow = true;
+               char *sl;
+
+               if (*tok == '!') {
+                       allow = false;
+                       tok++;
+               }
+
+               if (*tok == '\0')
+                       continue;
+
+               sl = strchr(tok, '/');
+               if (sl) {
+                       *sl++ = '\0';
+                       if (strcmp(tok, caller)) {
+                               if (allow)
+                                       result = false;
+                               continue;
+                       }
+                       tok = sl;
+               }
+
+               if (strcmp(tok, name)) {
+                       if (allow)
+                               result = false;
+                       continue;
+               }
+
+               result = allow;
+               break;
+       }
+       kfree(filter);
+
+       return result;
+}
+
+int
+__subtests(const char *caller, const struct subtest *st, int count, void *data)
+{
+       int err;
+
+       for (; count--; st++) {
+               cond_resched();
+               if (signal_pending(current))
+                       return -EINTR;
+
+               if (!apply_subtest_filter(caller, st->name))
+                       continue;
+
+               pr_info("dma-buf: Running %s/%s\n", caller, st->name);
+
+               err = st->func(data);
+               if (err && err != -EINTR) {
+                       pr_err("dma-buf/%s: %s failed with error %d\n",
+                              caller, st->name, err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static void set_default_test_all(struct selftest *st, unsigned long count)
+{
+       unsigned long i;
+
+       for (i = 0; i < count; i++)
+               if (st[i].enabled)
+                       return;
+
+       for (i = 0; i < count; i++)
+               st[i].enabled = true;
+}
+
+static int run_selftests(struct selftest *st, unsigned long count)
+{
+       int err = 0;
+
+       set_default_test_all(st, count);
+
+       /* Tests are listed in natural order in selftests.h */
+       for (; count--; st++) {
+               if (!st->enabled)
+                       continue;
+
+               pr_info("dma-buf: Running %s\n", st->name);
+               err = st->func();
+               if (err)
+                       break;
+       }
+
+       if (WARN(err > 0 || err == -ENOTTY,
+                "%s returned %d, conflicting with selftest's magic values!\n",
+                st->name, err))
+               err = -1;
+
+       return err;
+}
+
+static int __init st_init(void)
+{
+       return run_selftests(selftests, ARRAY_SIZE(selftests));
+}
+
+static void __exit st_exit(void)
+{
+}
+
+module_param_named(st_filter, __st_filter, charp, 0400);
+module_init(st_init);
+module_exit(st_exit);
+
+MODULE_DESCRIPTION("Self-test harness for dma-buf");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/dma-buf/selftest.h b/drivers/dma-buf/selftest.h
new file mode 100644 (file)
index 0000000..45793af
--- /dev/null
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: MIT
+
+/*
+ * Copyright Â© 2019 Intel Corporation
+ */
+
+#ifndef __SELFTEST_H__
+#define __SELFTEST_H__
+
+#include <linux/compiler.h>
+
+#define selftest(name, func) int func(void);
+#include "selftests.h"
+#undef selftest
+
+struct subtest {
+       int (*func)(void *data);
+       const char *name;
+};
+
+int __subtests(const char *caller,
+              const struct subtest *st,
+              int count,
+              void *data);
+#define subtests(T, data) \
+       __subtests(__func__, T, ARRAY_SIZE(T), data)
+
+#define SUBTEST(x) { x, #x }
+
+#endif /* __SELFTEST_H__ */
diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h
new file mode 100644 (file)
index 0000000..5320386
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+/* List each unit test as selftest(name, function)
+ *
+ * The name is used as both an enum and expanded as subtest__name to create
+ * a module parameter. It must be unique and legal for a C identifier.
+ *
+ * The function should be of type int function(void). It may be conditionally
+ * compiled using #if IS_ENABLED(DRM_I915_SELFTEST).
+ *
+ * Tests are executed in order by igt/dmabuf_selftest
+ */
+selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
+selftest(dma_fence, dma_fence)
diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c
new file mode 100644 (file)
index 0000000..e593064
--- /dev/null
@@ -0,0 +1,574 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * Copyright Â© 2019 Intel Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-fence.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "selftest.h"
+
+static struct kmem_cache *slab_fences;
+
+static struct mock_fence {
+       struct dma_fence base;
+       struct spinlock lock;
+} *to_mock_fence(struct dma_fence *f) {
+       return container_of(f, struct mock_fence, base);
+}
+
+static const char *mock_name(struct dma_fence *f)
+{
+       return "mock";
+}
+
+static void mock_fence_release(struct dma_fence *f)
+{
+       kmem_cache_free(slab_fences, to_mock_fence(f));
+}
+
+struct wait_cb {
+       struct dma_fence_cb cb;
+       struct task_struct *task;
+};
+
+static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+       wake_up_process(container_of(cb, struct wait_cb, cb)->task);
+}
+
+static long mock_wait(struct dma_fence *f, bool intr, long timeout)
+{
+       const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
+       struct wait_cb cb = { .task = current };
+
+       if (dma_fence_add_callback(f, &cb.cb, mock_wakeup))
+               return timeout;
+
+       while (timeout) {
+               set_current_state(state);
+
+               if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags))
+                       break;
+
+               if (signal_pending_state(state, current))
+                       break;
+
+               timeout = schedule_timeout(timeout);
+       }
+       __set_current_state(TASK_RUNNING);
+
+       if (!dma_fence_remove_callback(f, &cb.cb))
+               return timeout;
+
+       if (signal_pending_state(state, current))
+               return -ERESTARTSYS;
+
+       return -ETIME;
+}
+
+static const struct dma_fence_ops mock_ops = {
+       .get_driver_name = mock_name,
+       .get_timeline_name = mock_name,
+       .wait = mock_wait,
+       .release = mock_fence_release,
+};
+
+static struct dma_fence *mock_fence(void)
+{
+       struct mock_fence *f;
+
+       f = kmem_cache_alloc(slab_fences, GFP_KERNEL);
+       if (!f)
+               return NULL;
+
+       spin_lock_init(&f->lock);
+       dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
+
+       return &f->base;
+}
+
+static int sanitycheck(void *arg)
+{
+       struct dma_fence *f;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       dma_fence_signal(f);
+       dma_fence_put(f);
+
+       return 0;
+}
+
+static int test_signaling(void *arg)
+{
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       if (dma_fence_is_signaled(f)) {
+               pr_err("Fence unexpectedly signaled on creation\n");
+               goto err_free;
+       }
+
+       if (dma_fence_signal(f)) {
+               pr_err("Fence reported being already signaled\n");
+               goto err_free;
+       }
+
+       if (!dma_fence_is_signaled(f)) {
+               pr_err("Fence not reporting signaled\n");
+               goto err_free;
+       }
+
+       if (!dma_fence_signal(f)) {
+               pr_err("Fence reported not being already signaled\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+struct simple_cb {
+       struct dma_fence_cb cb;
+       bool seen;
+};
+
+static void simple_callback(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+       smp_store_mb(container_of(cb, struct simple_cb, cb)->seen, true);
+}
+
+static int test_add_callback(void *arg)
+{
+       struct simple_cb cb = {};
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
+               pr_err("Failed to add callback, fence already signaled!\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+       if (!cb.seen) {
+               pr_err("Callback failed!\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+static int test_late_add_callback(void *arg)
+{
+       struct simple_cb cb = {};
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       dma_fence_signal(f);
+
+       if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) {
+               pr_err("Added callback, but fence was already signaled!\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+       if (cb.seen) {
+               pr_err("Callback called after failed attachment !\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+static int test_rm_callback(void *arg)
+{
+       struct simple_cb cb = {};
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
+               pr_err("Failed to add callback, fence already signaled!\n");
+               goto err_free;
+       }
+
+       if (!dma_fence_remove_callback(f, &cb.cb)) {
+               pr_err("Failed to remove callback!\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+       if (cb.seen) {
+               pr_err("Callback still signaled after removal!\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+static int test_late_rm_callback(void *arg)
+{
+       struct simple_cb cb = {};
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
+               pr_err("Failed to add callback, fence already signaled!\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+       if (!cb.seen) {
+               pr_err("Callback failed!\n");
+               goto err_free;
+       }
+
+       if (dma_fence_remove_callback(f, &cb.cb)) {
+               pr_err("Callback removal succeed after being executed!\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+static int test_status(void *arg)
+{
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       if (dma_fence_get_status(f)) {
+               pr_err("Fence unexpectedly has signaled status on creation\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+       if (!dma_fence_get_status(f)) {
+               pr_err("Fence not reporting signaled status\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+static int test_error(void *arg)
+{
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       dma_fence_set_error(f, -EIO);
+
+       if (dma_fence_get_status(f)) {
+               pr_err("Fence unexpectedly has error status before signal\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+       if (dma_fence_get_status(f) != -EIO) {
+               pr_err("Fence not reporting error status, got %d\n",
+                      dma_fence_get_status(f));
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_put(f);
+       return err;
+}
+
+static int test_wait(void *arg)
+{
+       struct dma_fence *f;
+       int err = -EINVAL;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       if (dma_fence_wait_timeout(f, false, 0) != -ETIME) {
+               pr_err("Wait reported complete before being signaled\n");
+               goto err_free;
+       }
+
+       dma_fence_signal(f);
+
+       if (dma_fence_wait_timeout(f, false, 0) != 0) {
+               pr_err("Wait reported incomplete after being signaled\n");
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       dma_fence_signal(f);
+       dma_fence_put(f);
+       return err;
+}
+
+struct wait_timer {
+       struct timer_list timer;
+       struct dma_fence *f;
+};
+
+static void wait_timer(struct timer_list *timer)
+{
+       struct wait_timer *wt = from_timer(wt, timer, timer);
+
+       dma_fence_signal(wt->f);
+}
+
+static int test_wait_timeout(void *arg)
+{
+       struct wait_timer wt;
+       int err = -EINVAL;
+
+       timer_setup_on_stack(&wt.timer, wait_timer, 0);
+
+       wt.f = mock_fence();
+       if (!wt.f)
+               return -ENOMEM;
+
+       if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) {
+               pr_err("Wait reported complete before being signaled\n");
+               goto err_free;
+       }
+
+       mod_timer(&wt.timer, jiffies + 1);
+
+       if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) {
+               if (timer_pending(&wt.timer)) {
+                       pr_notice("Timer did not fire within the jiffie!\n");
+                       err = 0; /* not our fault! */
+               } else {
+                       pr_err("Wait reported incomplete after timeout\n");
+               }
+               goto err_free;
+       }
+
+       err = 0;
+err_free:
+       del_timer_sync(&wt.timer);
+       destroy_timer_on_stack(&wt.timer);
+       dma_fence_signal(wt.f);
+       dma_fence_put(wt.f);
+       return err;
+}
+
+static int test_stub(void *arg)
+{
+       struct dma_fence *f[64];
+       int err = -EINVAL;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(f); i++) {
+               f[i] = dma_fence_get_stub();
+               if (!dma_fence_is_signaled(f[i])) {
+                       pr_err("Obtained unsignaled stub fence!\n");
+                       goto err;
+               }
+       }
+
+       err = 0;
+err:
+       while (i--)
+               dma_fence_put(f[i]);
+       return err;
+}
+
+/* Now off to the races! */
+
+struct race_thread {
+       struct dma_fence __rcu **fences;
+       struct task_struct *task;
+       bool before;
+       int id;
+};
+
+static void __wait_for_callbacks(struct dma_fence *f)
+{
+       spin_lock_irq(f->lock);
+       spin_unlock_irq(f->lock);
+}
+
+static int thread_signal_callback(void *arg)
+{
+       const struct race_thread *t = arg;
+       unsigned long pass = 0;
+       unsigned long miss = 0;
+       int err = 0;
+
+       while (!err && !kthread_should_stop()) {
+               struct dma_fence *f1, *f2;
+               struct simple_cb cb;
+
+               f1 = mock_fence();
+               if (!f1) {
+                       err = -ENOMEM;
+                       break;
+               }
+
+               rcu_assign_pointer(t->fences[t->id], f1);
+               smp_wmb();
+
+               rcu_read_lock();
+               do {
+                       f2 = dma_fence_get_rcu_safe(&t->fences[!t->id]);
+               } while (!f2 && !kthread_should_stop());
+               rcu_read_unlock();
+
+               if (t->before)
+                       dma_fence_signal(f1);
+
+               smp_store_mb(cb.seen, false);
+               if (!f2 || dma_fence_add_callback(f2, &cb.cb, simple_callback))
+                       miss++, cb.seen = true;
+
+               if (!t->before)
+                       dma_fence_signal(f1);
+
+               if (!cb.seen) {
+                       dma_fence_wait(f2, false);
+                       __wait_for_callbacks(f2);
+               }
+
+               if (!READ_ONCE(cb.seen)) {
+                       pr_err("Callback not seen on thread %d, pass %lu (%lu misses), signaling %s add_callback; fence signaled? %s\n",
+                              t->id, pass, miss,
+                              t->before ? "before" : "after",
+                              dma_fence_is_signaled(f2) ? "yes" : "no");
+                       err = -EINVAL;
+               }
+
+               dma_fence_put(f2);
+
+               rcu_assign_pointer(t->fences[t->id], NULL);
+               smp_wmb();
+
+               dma_fence_put(f1);
+
+               pass++;
+       }
+
+       pr_info("%s[%d] completed %lu passes, %lu misses\n",
+               __func__, t->id, pass, miss);
+       return err;
+}
+
+static int race_signal_callback(void *arg)
+{
+       struct dma_fence __rcu *f[2] = {};
+       int ret = 0;
+       int pass;
+
+       for (pass = 0; !ret && pass <= 1; pass++) {
+               struct race_thread t[2];
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(t); i++) {
+                       t[i].fences = f;
+                       t[i].id = i;
+                       t[i].before = pass;
+                       t[i].task = kthread_run(thread_signal_callback, &t[i],
+                                               "dma-fence:%d", i);
+                       get_task_struct(t[i].task);
+               }
+
+               msleep(50);
+
+               for (i = 0; i < ARRAY_SIZE(t); i++) {
+                       int err;
+
+                       err = kthread_stop(t[i].task);
+                       if (err && !ret)
+                               ret = err;
+
+                       put_task_struct(t[i].task);
+               }
+       }
+
+       return ret;
+}
+
+int dma_fence(void)
+{
+       static const struct subtest tests[] = {
+               SUBTEST(sanitycheck),
+               SUBTEST(test_signaling),
+               SUBTEST(test_add_callback),
+               SUBTEST(test_late_add_callback),
+               SUBTEST(test_rm_callback),
+               SUBTEST(test_late_rm_callback),
+               SUBTEST(test_status),
+               SUBTEST(test_error),
+               SUBTEST(test_wait),
+               SUBTEST(test_wait_timeout),
+               SUBTEST(test_stub),
+               SUBTEST(race_signal_callback),
+       };
+       int ret;
+
+       pr_info("sizeof(dma_fence)=%zu\n", sizeof(struct dma_fence));
+
+       slab_fences = KMEM_CACHE(mock_fence,
+                                SLAB_TYPESAFE_BY_RCU |
+                                SLAB_HWCACHE_ALIGN);
+       if (!slab_fences)
+               return -ENOMEM;
+
+       ret = subtests(tests, NULL);
+
+       kmem_cache_destroy(slab_fences);
+
+       return ret;
+}
index 1ff7f4b..0142ee9 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/iommu.h>
 #include <linux/of_device.h>
 #include <linux/of_graph.h>
+#include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #ifdef CONFIG_DEBUG_FS
@@ -146,6 +147,12 @@ static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
                return mdev->irq;
        }
 
+       /* Get the optional framebuffer memory resource */
+       ret = of_reserved_mem_device_init(dev);
+       if (ret && ret != -ENODEV)
+               return ret;
+       ret = 0;
+
        for_each_available_child_of_node(np, child) {
                if (of_node_cmp(child->name, "pipeline") == 0) {
                        ret = komeda_parse_pipe_dt(mdev, child);
@@ -292,6 +299,8 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
 
        mdev->n_pipelines = 0;
 
+       of_reserved_mem_device_release(dev);
+
        if (funcs && funcs->cleanup)
                funcs->cleanup(mdev);
 
index 0f94997..ac1e001 100644 (file)
@@ -256,8 +256,8 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev)
        dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY);
 
        cec->adap = cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi",
-                                        CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
-                                        CEC_CAP_RC | CEC_CAP_PASSTHROUGH,
+                                        CEC_CAP_DEFAULTS |
+                                        CEC_CAP_CONNECTOR_INFO,
                                         CEC_MAX_LOG_ADDRS);
        if (IS_ERR(cec->adap))
                return PTR_ERR(cec->adap);
@@ -278,13 +278,14 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
-       cec->notify = cec_notifier_get(pdev->dev.parent);
+       cec->notify = cec_notifier_cec_adap_register(pdev->dev.parent,
+                                                    NULL, cec->adap);
        if (!cec->notify)
                return -ENOMEM;
 
        ret = cec_register_adapter(cec->adap, pdev->dev.parent);
        if (ret < 0) {
-               cec_notifier_put(cec->notify);
+               cec_notifier_cec_adap_unregister(cec->notify);
                return ret;
        }
 
@@ -294,8 +295,6 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev)
         */
        devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec);
 
-       cec_register_cec_notifier(cec->adap, cec->notify);
-
        return 0;
 }
 
@@ -303,8 +302,8 @@ static int dw_hdmi_cec_remove(struct platform_device *pdev)
 {
        struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
 
+       cec_notifier_cec_adap_unregister(cec->notify);
        cec_unregister_adapter(cec->adap);
-       cec_notifier_put(cec->notify);
 
        return 0;
 }
index a4685d0..521d689 100644 (file)
@@ -189,6 +189,7 @@ struct dw_hdmi {
        void (*enable_audio)(struct dw_hdmi *hdmi);
        void (*disable_audio)(struct dw_hdmi *hdmi);
 
+       struct mutex cec_notifier_mutex;
        struct cec_notifier *cec_notifier;
 };
 
@@ -2229,6 +2230,8 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
        struct dw_hdmi *hdmi = bridge->driver_private;
        struct drm_encoder *encoder = bridge->encoder;
        struct drm_connector *connector = &hdmi->connector;
+       struct cec_connector_info conn_info;
+       struct cec_notifier *notifier;
 
        connector->interlace_allowed = 1;
        connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -2242,9 +2245,29 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
 
        drm_connector_attach_encoder(connector, encoder);
 
+       cec_fill_conn_info_from_drm(&conn_info, connector);
+
+       notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
+       if (!notifier)
+               return -ENOMEM;
+
+       mutex_lock(&hdmi->cec_notifier_mutex);
+       hdmi->cec_notifier = notifier;
+       mutex_unlock(&hdmi->cec_notifier_mutex);
+
        return 0;
 }
 
+static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
+{
+       struct dw_hdmi *hdmi = bridge->driver_private;
+
+       mutex_lock(&hdmi->cec_notifier_mutex);
+       cec_notifier_conn_unregister(hdmi->cec_notifier);
+       hdmi->cec_notifier = NULL;
+       mutex_unlock(&hdmi->cec_notifier_mutex);
+}
+
 static enum drm_mode_status
 dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
                          const struct drm_display_mode *mode)
@@ -2301,6 +2324,7 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 
 static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
        .attach = dw_hdmi_bridge_attach,
+       .detach = dw_hdmi_bridge_detach,
        .enable = dw_hdmi_bridge_enable,
        .disable = dw_hdmi_bridge_disable,
        .mode_set = dw_hdmi_bridge_mode_set,
@@ -2408,9 +2432,11 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
                                       phy_stat & HDMI_PHY_HPD,
                                       phy_stat & HDMI_PHY_RX_SENSE);
 
-               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
-                       cec_notifier_set_phys_addr(hdmi->cec_notifier,
-                                                  CEC_PHYS_ADDR_INVALID);
+               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) {
+                       mutex_lock(&hdmi->cec_notifier_mutex);
+                       cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
+                       mutex_unlock(&hdmi->cec_notifier_mutex);
+               }
        }
 
        if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
@@ -2596,6 +2622,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
 
        mutex_init(&hdmi->mutex);
        mutex_init(&hdmi->audio_mutex);
+       mutex_init(&hdmi->cec_notifier_mutex);
        spin_lock_init(&hdmi->audio_lock);
 
        ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
@@ -2728,12 +2755,6 @@ __dw_hdmi_probe(struct platform_device *pdev,
        if (ret)
                goto err_iahb;
 
-       hdmi->cec_notifier = cec_notifier_get(dev);
-       if (!hdmi->cec_notifier) {
-               ret = -ENOMEM;
-               goto err_iahb;
-       }
-
        /*
         * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
         * N and cts values before enabling phy
@@ -2832,9 +2853,6 @@ err_iahb:
                hdmi->ddc = NULL;
        }
 
-       if (hdmi->cec_notifier)
-               cec_notifier_put(hdmi->cec_notifier);
-
        clk_disable_unprepare(hdmi->iahb_clk);
        if (hdmi->cec_clk)
                clk_disable_unprepare(hdmi->cec_clk);
@@ -2856,9 +2874,6 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
        /* Disable all interrupts */
        hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-       if (hdmi->cec_notifier)
-               cec_notifier_put(hdmi->cec_notifier);
-
        clk_disable_unprepare(hdmi->iahb_clk);
        clk_disable_unprepare(hdmi->isfr_clk);
        if (hdmi->cec_clk)
index e652305..c456c3d 100644 (file)
@@ -328,11 +328,9 @@ void drm_minor_release(struct drm_minor *minor)
  *             struct drm_device *drm;
  *             int ret;
  *
- *             [
- *               devm_kzalloc() can't be used here because the drm_device
- *               lifetime can exceed the device lifetime if driver unbind
- *               happens when userspace still has open file descriptors.
- *             ]
+ *             // devm_kzalloc() can't be used here because the drm_device '
+ *             // lifetime can exceed the device lifetime if driver unbind
+ *             // happens when userspace still has open file descriptors.
  *             priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  *             if (!priv)
  *                     return -ENOMEM;
@@ -355,7 +353,7 @@ void drm_minor_release(struct drm_minor *minor)
  *             if (IS_ERR(priv->pclk))
  *                     return PTR_ERR(priv->pclk);
  *
- *             [ Further setup, display pipeline etc ]
+ *             // Further setup, display pipeline etc
  *
  *             platform_set_drvdata(pdev, drm);
  *
@@ -370,7 +368,7 @@ void drm_minor_release(struct drm_minor *minor)
  *             return 0;
  *     }
  *
- *     [ This function is called before the devm_ resources are released ]
+ *     // This function is called before the devm_ resources are released
  *     static int driver_remove(struct platform_device *pdev)
  *     {
  *             struct drm_device *drm = platform_get_drvdata(pdev);
@@ -381,7 +379,7 @@ void drm_minor_release(struct drm_minor *minor)
  *             return 0;
  *     }
  *
- *     [ This function is called on kernel restart and shutdown ]
+ *     // This function is called on kernel restart and shutdown
  *     static void driver_shutdown(struct platform_device *pdev)
  *     {
  *             drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
index d9a5ac8..221a852 100644 (file)
@@ -40,7 +40,7 @@ MODULE_LICENSE("GPL and additional rights");
 /* Backward compatibility for drm_kms_helper.edid_firmware */
 static int edid_firmware_set(const char *val, const struct kernel_param *kp)
 {
-       DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware instead.\n");
+       DRM_NOTE("drm_kms_helper.edid_firmware is deprecated, please use drm.edid_firmware instead.\n");
 
        return __drm_set_edid_firmware_path(val);
 }
index 0fa29af..290553e 100644 (file)
@@ -5,16 +5,8 @@ config DRM_HISI_KIRIN
        select DRM_KMS_HELPER
        select DRM_GEM_CMA_HELPER
        select DRM_KMS_CMA_HELPER
-       select HISI_KIRIN_DW_DSI
+       select DRM_MIPI_DSI
        help
          Choose this option if you have a hisilicon Kirin chipsets(hi6220).
          If M is selected the module will be called kirin-drm.
 
-config HISI_KIRIN_DW_DSI
-       tristate "HiSilicon Kirin specific extensions for Synopsys DW MIPI DSI"
-       depends on DRM_HISI_KIRIN
-       select DRM_MIPI_DSI
-       help
-        This selects support for HiSilicon Kirin SoC specific extensions for
-        the Synopsys DesignWare DSI driver. If you want to enable MIPI DSI on
-        hi6220 based SoC, you should selet this option.
index c0501fa..d9323f6 100644 (file)
@@ -2,6 +2,5 @@
 kirin-drm-y := kirin_drm_drv.o \
               kirin_drm_ade.o
 
-obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o dw_drm_dsi.o
 
-obj-$(CONFIG_HISI_KIRIN_DW_DSI) += dw_drm_dsi.o
index e2ac098..0da8602 100644 (file)
@@ -83,6 +83,7 @@
 #define VSIZE_OFST                     20
 #define LDI_INT_EN                     0x741C
 #define FRAME_END_INT_EN_OFST          1
+#define UNDERFLOW_INT_EN_OFST          2
 #define LDI_CTRL                       0x7420
 #define BPP_OFST                       3
 #define DATA_GATE_EN                   BIT(2)
index 0df1afd..73cd28a 100644 (file)
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 
 #include "kirin_drm_drv.h"
 #include "kirin_ade_reg.h"
 
-#define PRIMARY_CH     ADE_CH1 /* primary plane */
 #define OUT_OVLY       ADE_OVLY2 /* output overlay compositor */
 #define ADE_DEBUG      1
 
-#define to_ade_crtc(crtc) \
-       container_of(crtc, struct ade_crtc, base)
-
-#define to_ade_plane(plane) \
-       container_of(plane, struct ade_plane, base)
 
 struct ade_hw_ctx {
        void __iomem  *base;
@@ -51,36 +46,14 @@ struct ade_hw_ctx {
        struct clk *media_noc_clk;
        struct clk *ade_pix_clk;
        struct reset_control *reset;
+       struct work_struct display_reset_wq;
        bool power_on;
        int irq;
-};
-
-struct ade_crtc {
-       struct drm_crtc base;
-       struct ade_hw_ctx *ctx;
-       bool enable;
-       u32 out_format;
-};
-
-struct ade_plane {
-       struct drm_plane base;
-       void *ctx;
-       u8 ch; /* channel */
-};
 
-struct ade_data {
-       struct ade_crtc acrtc;
-       struct ade_plane aplane[ADE_CH_NUM];
-       struct ade_hw_ctx ctx;
+       struct drm_crtc *crtc;
 };
 
-/* ade-format info: */
-struct ade_format {
-       u32 pixel_format;
-       enum ade_fb_format ade_format;
-};
-
-static const struct ade_format ade_formats[] = {
+static const struct kirin_format ade_formats[] = {
        /* 16bpp RGB: */
        { DRM_FORMAT_RGB565, ADE_RGB_565 },
        { DRM_FORMAT_BGR565, ADE_BGR_565 },
@@ -96,7 +69,7 @@ static const struct ade_format ade_formats[] = {
        { DRM_FORMAT_ABGR8888, ADE_ABGR_8888 },
 };
 
-static const u32 channel_formats1[] = {
+static const u32 channel_formats[] = {
        /* channel 1,2,3,4 */
        DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
        DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
@@ -104,19 +77,6 @@ static const u32 channel_formats1[] = {
        DRM_FORMAT_ABGR8888
 };
 
-u32 ade_get_channel_formats(u8 ch, const u32 **formats)
-{
-       switch (ch) {
-       case ADE_CH1:
-               *formats = channel_formats1;
-               return ARRAY_SIZE(channel_formats1);
-       default:
-               DRM_ERROR("no this channel %d\n", ch);
-               *formats = NULL;
-               return 0;
-       }
-}
-
 /* convert from fourcc format to ade format */
 static u32 ade_get_format(u32 pixel_format)
 {
@@ -124,7 +84,7 @@ static u32 ade_get_format(u32 pixel_format)
 
        for (i = 0; i < ARRAY_SIZE(ade_formats); i++)
                if (ade_formats[i].pixel_format == pixel_format)
-                       return ade_formats[i].ade_format;
+                       return ade_formats[i].hw_format;
 
        /* not found */
        DRM_ERROR("Not found pixel format!!fourcc_format= %d\n",
@@ -176,14 +136,15 @@ static void ade_init(struct ade_hw_ctx *ctx)
         */
        ade_update_bits(base + ADE_CTRL, FRM_END_START_OFST,
                        FRM_END_START_MASK, REG_EFFECTIVE_IN_ADEEN_FRMEND);
+       ade_update_bits(base + LDI_INT_EN, UNDERFLOW_INT_EN_OFST, MASK(1), 1);
 }
 
 static bool ade_crtc_mode_fixup(struct drm_crtc *crtc,
                                const struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
 
        adjusted_mode->clock =
                clk_round_rate(ctx->ade_pix_clk, mode->clock * 1000) / 1000;
@@ -208,11 +169,10 @@ static void ade_set_pix_clk(struct ade_hw_ctx *ctx,
        adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
 }
 
-static void ade_ldi_set_mode(struct ade_crtc *acrtc,
+static void ade_ldi_set_mode(struct ade_hw_ctx *ctx,
                             struct drm_display_mode *mode,
                             struct drm_display_mode *adj_mode)
 {
-       struct ade_hw_ctx *ctx = acrtc->ctx;
        void __iomem *base = ctx->base;
        u32 width = mode->hdisplay;
        u32 height = mode->vdisplay;
@@ -299,9 +259,8 @@ static void ade_power_down(struct ade_hw_ctx *ctx)
        ctx->power_on = false;
 }
 
-static void ade_set_medianoc_qos(struct ade_crtc *acrtc)
+static void ade_set_medianoc_qos(struct ade_hw_ctx *ctx)
 {
-       struct ade_hw_ctx *ctx = acrtc->ctx;
        struct regmap *map = ctx->noc_regmap;
 
        regmap_update_bits(map, ADE0_QOSGENERATOR_MODE,
@@ -317,8 +276,8 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc)
 
 static int ade_crtc_enable_vblank(struct drm_crtc *crtc)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
        void __iomem *base = ctx->base;
 
        if (!ctx->power_on)
@@ -332,8 +291,8 @@ static int ade_crtc_enable_vblank(struct drm_crtc *crtc)
 
 static void ade_crtc_disable_vblank(struct drm_crtc *crtc)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
        void __iomem *base = ctx->base;
 
        if (!ctx->power_on) {
@@ -345,11 +304,21 @@ static void ade_crtc_disable_vblank(struct drm_crtc *crtc)
                        MASK(1), 0);
 }
 
+static void drm_underflow_wq(struct work_struct *work)
+{
+       struct ade_hw_ctx *ctx = container_of(work, struct ade_hw_ctx,
+                                             display_reset_wq);
+       struct drm_device *drm_dev = ctx->crtc->dev;
+       struct drm_atomic_state *state;
+
+       state = drm_atomic_helper_suspend(drm_dev);
+       drm_atomic_helper_resume(drm_dev, state);
+}
+
 static irqreturn_t ade_irq_handler(int irq, void *data)
 {
-       struct ade_crtc *acrtc = data;
-       struct ade_hw_ctx *ctx = acrtc->ctx;
-       struct drm_crtc *crtc = &acrtc->base;
+       struct ade_hw_ctx *ctx = data;
+       struct drm_crtc *crtc = ctx->crtc;
        void __iomem *base = ctx->base;
        u32 status;
 
@@ -362,15 +331,20 @@ static irqreturn_t ade_irq_handler(int irq, void *data)
                                MASK(1), 1);
                drm_crtc_handle_vblank(crtc);
        }
+       if (status & BIT(UNDERFLOW_INT_EN_OFST)) {
+               ade_update_bits(base + LDI_INT_CLR, UNDERFLOW_INT_EN_OFST,
+                               MASK(1), 1);
+               DRM_ERROR("LDI underflow!");
+               schedule_work(&ctx->display_reset_wq);
+       }
 
        return IRQ_HANDLED;
 }
 
-static void ade_display_enable(struct ade_crtc *acrtc)
+static void ade_display_enable(struct ade_hw_ctx *ctx)
 {
-       struct ade_hw_ctx *ctx = acrtc->ctx;
        void __iomem *base = ctx->base;
-       u32 out_fmt = acrtc->out_format;
+       u32 out_fmt = LDI_OUT_RGB_888;
 
        /* enable output overlay compositor */
        writel(ADE_ENABLE, base + ADE_OVLYX_CTL(OUT_OVLY));
@@ -483,11 +457,11 @@ static void ade_dump_regs(void __iomem *base) { }
 static void ade_crtc_atomic_enable(struct drm_crtc *crtc,
                                   struct drm_crtc_state *old_state)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
        int ret;
 
-       if (acrtc->enable)
+       if (kcrtc->enable)
                return;
 
        if (!ctx->power_on) {
@@ -496,63 +470,63 @@ static void ade_crtc_atomic_enable(struct drm_crtc *crtc,
                        return;
        }
 
-       ade_set_medianoc_qos(acrtc);
-       ade_display_enable(acrtc);
+       ade_set_medianoc_qos(ctx);
+       ade_display_enable(ctx);
        ade_dump_regs(ctx->base);
        drm_crtc_vblank_on(crtc);
-       acrtc->enable = true;
+       kcrtc->enable = true;
 }
 
 static void ade_crtc_atomic_disable(struct drm_crtc *crtc,
                                    struct drm_crtc_state *old_state)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
 
-       if (!acrtc->enable)
+       if (!kcrtc->enable)
                return;
 
        drm_crtc_vblank_off(crtc);
        ade_power_down(ctx);
-       acrtc->enable = false;
+       kcrtc->enable = false;
 }
 
 static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
        struct drm_display_mode *mode = &crtc->state->mode;
        struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode;
 
        if (!ctx->power_on)
                (void)ade_power_up(ctx);
-       ade_ldi_set_mode(acrtc, mode, adj_mode);
+       ade_ldi_set_mode(ctx, mode, adj_mode);
 }
 
 static void ade_crtc_atomic_begin(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_state)
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
        struct drm_display_mode *mode = &crtc->state->mode;
        struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode;
 
        if (!ctx->power_on)
                (void)ade_power_up(ctx);
-       ade_ldi_set_mode(acrtc, mode, adj_mode);
+       ade_ldi_set_mode(ctx, mode, adj_mode);
 }
 
 static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_state)
 
 {
-       struct ade_crtc *acrtc = to_ade_crtc(crtc);
-       struct ade_hw_ctx *ctx = acrtc->ctx;
+       struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
+       struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
        struct drm_pending_vblank_event *event = crtc->state->event;
        void __iomem *base = ctx->base;
 
        /* only crtc is enabled regs take effect */
-       if (acrtc->enable) {
+       if (kcrtc->enable) {
                ade_dump_regs(base);
                /* flush ade registers */
                writel(ADE_ENABLE, base + ADE_EN);
@@ -590,35 +564,6 @@ static const struct drm_crtc_funcs ade_crtc_funcs = {
        .disable_vblank = ade_crtc_disable_vblank,
 };
 
-static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
-                        struct drm_plane *plane)
-{
-       struct device_node *port;
-       int ret;
-
-       /* set crtc port so that
-        * drm_of_find_possible_crtcs call works
-        */
-       port = of_get_child_by_name(dev->dev->of_node, "port");
-       if (!port) {
-               DRM_ERROR("no port node found in %pOF\n", dev->dev->of_node);
-               return -EINVAL;
-       }
-       of_node_put(port);
-       crtc->port = port;
-
-       ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
-                                       &ade_crtc_funcs, NULL);
-       if (ret) {
-               DRM_ERROR("failed to init crtc.\n");
-               return ret;
-       }
-
-       drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
-
-       return 0;
-}
-
 static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb,
                         u32 ch, u32 y, u32 in_h, u32 fmt)
 {
@@ -780,16 +725,16 @@ static void ade_compositor_routing_disable(void __iomem *base, u32 ch)
 /*
  * Typicaly, a channel looks like: DMA-->clip-->scale-->ctrans-->compositor
  */
-static void ade_update_channel(struct ade_plane *aplane,
+static void ade_update_channel(struct kirin_plane *kplane,
                               struct drm_framebuffer *fb, int crtc_x,
                               int crtc_y, unsigned int crtc_w,
                               unsigned int crtc_h, u32 src_x,
                               u32 src_y, u32 src_w, u32 src_h)
 {
-       struct ade_hw_ctx *ctx = aplane->ctx;
+       struct ade_hw_ctx *ctx = kplane->hw_ctx;
        void __iomem *base = ctx->base;
        u32 fmt = ade_get_format(fb->format->format);
-       u32 ch = aplane->ch;
+       u32 ch = kplane->ch;
        u32 in_w;
        u32 in_h;
 
@@ -813,11 +758,11 @@ static void ade_update_channel(struct ade_plane *aplane,
        ade_compositor_routing_set(base, ch, crtc_x, crtc_y, in_w, in_h, fmt);
 }
 
-static void ade_disable_channel(struct ade_plane *aplane)
+static void ade_disable_channel(struct kirin_plane *kplane)
 {
-       struct ade_hw_ctx *ctx = aplane->ctx;
+       struct ade_hw_ctx *ctx = kplane->hw_ctx;
        void __iomem *base = ctx->base;
-       u32 ch = aplane->ch;
+       u32 ch = kplane->ch;
 
        DRM_DEBUG_DRIVER("disable channel%d\n", ch + 1);
 
@@ -879,10 +824,10 @@ static int ade_plane_atomic_check(struct drm_plane *plane,
 static void ade_plane_atomic_update(struct drm_plane *plane,
                                    struct drm_plane_state *old_state)
 {
-       struct drm_plane_state  *state  = plane->state;
-       struct ade_plane *aplane = to_ade_plane(plane);
+       struct drm_plane_state *state = plane->state;
+       struct kirin_plane *kplane = to_kirin_plane(plane);
 
-       ade_update_channel(aplane, state->fb, state->crtc_x, state->crtc_y,
+       ade_update_channel(kplane, state->fb, state->crtc_x, state->crtc_y,
                           state->crtc_w, state->crtc_h,
                           state->src_x >> 16, state->src_y >> 16,
                           state->src_w >> 16, state->src_h >> 16);
@@ -891,9 +836,9 @@ static void ade_plane_atomic_update(struct drm_plane *plane,
 static void ade_plane_atomic_disable(struct drm_plane *plane,
                                     struct drm_plane_state *old_state)
 {
-       struct ade_plane *aplane = to_ade_plane(plane);
+       struct kirin_plane *kplane = to_kirin_plane(plane);
 
-       ade_disable_channel(aplane);
+       ade_disable_channel(kplane);
 }
 
 static const struct drm_plane_helper_funcs ade_plane_helper_funcs = {
@@ -911,144 +856,124 @@ static struct drm_plane_funcs ade_plane_funcs = {
        .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
-static int ade_plane_init(struct drm_device *dev, struct ade_plane *aplane,
-                         enum drm_plane_type type)
-{
-       const u32 *fmts;
-       u32 fmts_cnt;
-       int ret = 0;
-
-       /* get  properties */
-       fmts_cnt = ade_get_channel_formats(aplane->ch, &fmts);
-       if (ret)
-               return ret;
-
-       ret = drm_universal_plane_init(dev, &aplane->base, 1, &ade_plane_funcs,
-                                      fmts, fmts_cnt, NULL, type, NULL);
-       if (ret) {
-               DRM_ERROR("fail to init plane, ch=%d\n", aplane->ch);
-               return ret;
-       }
-
-       drm_plane_helper_add(&aplane->base, &ade_plane_helper_funcs);
-
-       return 0;
-}
-
-static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx)
+static void *ade_hw_ctx_alloc(struct platform_device *pdev,
+                             struct drm_crtc *crtc)
 {
        struct resource *res;
        struct device *dev = &pdev->dev;
        struct device_node *np = pdev->dev.of_node;
+       struct ade_hw_ctx *ctx = NULL;
+       int ret;
+
+       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               DRM_ERROR("failed to alloc ade_hw_ctx\n");
+               return ERR_PTR(-ENOMEM);
+       }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ctx->base = devm_ioremap_resource(dev, res);
        if (IS_ERR(ctx->base)) {
                DRM_ERROR("failed to remap ade io base\n");
-               return  PTR_ERR(ctx->base);
+               return ERR_PTR(-EIO);
        }
 
        ctx->reset = devm_reset_control_get(dev, NULL);
        if (IS_ERR(ctx->reset))
-               return PTR_ERR(ctx->reset);
+               return ERR_PTR(-ENODEV);
 
        ctx->noc_regmap =
                syscon_regmap_lookup_by_phandle(np, "hisilicon,noc-syscon");
        if (IS_ERR(ctx->noc_regmap)) {
                DRM_ERROR("failed to get noc regmap\n");
-               return PTR_ERR(ctx->noc_regmap);
+               return ERR_PTR(-ENODEV);
        }
 
        ctx->irq = platform_get_irq(pdev, 0);
        if (ctx->irq < 0) {
                DRM_ERROR("failed to get irq\n");
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
        }
 
        ctx->ade_core_clk = devm_clk_get(dev, "clk_ade_core");
        if (IS_ERR(ctx->ade_core_clk)) {
                DRM_ERROR("failed to parse clk ADE_CORE\n");
-               return PTR_ERR(ctx->ade_core_clk);
+               return ERR_PTR(-ENODEV);
        }
 
        ctx->media_noc_clk = devm_clk_get(dev, "clk_codec_jpeg");
        if (IS_ERR(ctx->media_noc_clk)) {
                DRM_ERROR("failed to parse clk CODEC_JPEG\n");
-               return PTR_ERR(ctx->media_noc_clk);
+               return ERR_PTR(-ENODEV);
        }
 
        ctx->ade_pix_clk = devm_clk_get(dev, "clk_ade_pix");
        if (IS_ERR(ctx->ade_pix_clk)) {
                DRM_ERROR("failed to parse clk ADE_PIX\n");
-               return PTR_ERR(ctx->ade_pix_clk);
+               return ERR_PTR(-ENODEV);
        }
 
-       return 0;
-}
-
-static int ade_drm_init(struct platform_device *pdev)
-{
-       struct drm_device *dev = platform_get_drvdata(pdev);
-       struct ade_data *ade;
-       struct ade_hw_ctx *ctx;
-       struct ade_crtc *acrtc;
-       struct ade_plane *aplane;
-       enum drm_plane_type type;
-       int ret;
-       int i;
-
-       ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL);
-       if (!ade) {
-               DRM_ERROR("failed to alloc ade_data\n");
-               return -ENOMEM;
-       }
-       platform_set_drvdata(pdev, ade);
-
-       ctx = &ade->ctx;
-       acrtc = &ade->acrtc;
-       acrtc->ctx = ctx;
-       acrtc->out_format = LDI_OUT_RGB_888;
-
-       ret = ade_dts_parse(pdev, ctx);
-       if (ret)
-               return ret;
-
-       /*
-        * plane init
-        * TODO: Now only support primary plane, overlay planes
-        * need to do.
-        */
-       for (i = 0; i < ADE_CH_NUM; i++) {
-               aplane = &ade->aplane[i];
-               aplane->ch = i;
-               aplane->ctx = ctx;
-               type = i == PRIMARY_CH ? DRM_PLANE_TYPE_PRIMARY :
-                       DRM_PLANE_TYPE_OVERLAY;
-
-               ret = ade_plane_init(dev, aplane, type);
-               if (ret)
-                       return ret;
-       }
-
-       /* crtc init */
-       ret = ade_crtc_init(dev, &acrtc->base, &ade->aplane[PRIMARY_CH].base);
-       if (ret)
-               return ret;
-
        /* vblank irq init */
-       ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
-                              IRQF_SHARED, dev->driver->name, acrtc);
+       ret = devm_request_irq(dev, ctx->irq, ade_irq_handler,
+                              IRQF_SHARED, dev->driver->name, ctx);
        if (ret)
-               return ret;
+               return ERR_PTR(-EIO);
 
-       return 0;
+       INIT_WORK(&ctx->display_reset_wq, drm_underflow_wq);
+       ctx->crtc = crtc;
+
+       return ctx;
 }
 
-static void ade_drm_cleanup(struct platform_device *pdev)
+static void ade_hw_ctx_cleanup(void *hw_ctx)
 {
 }
 
-const struct kirin_dc_ops ade_dc_ops = {
-       .init = ade_drm_init,
-       .cleanup = ade_drm_cleanup
+static const struct drm_mode_config_funcs ade_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+
+};
+
+DEFINE_DRM_GEM_CMA_FOPS(ade_fops);
+
+static struct drm_driver ade_driver = {
+       .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+       .fops = &ade_fops,
+       .gem_free_object_unlocked = drm_gem_cma_free_object,
+       .gem_vm_ops = &drm_gem_cma_vm_ops,
+       .dumb_create = drm_gem_cma_dumb_create_internal,
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
+       .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_vmap = drm_gem_cma_prime_vmap,
+       .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+       .gem_prime_mmap = drm_gem_cma_prime_mmap,
+
+       .name = "kirin",
+       .desc = "Hisilicon Kirin620 SoC DRM Driver",
+       .date = "20150718",
+       .major = 1,
+       .minor = 0,
+};
+
+struct kirin_drm_data ade_driver_data = {
+       .register_connects = false,
+       .num_planes = ADE_CH_NUM,
+       .prim_plane = ADE_CH1,
+       .channel_formats = channel_formats,
+       .channel_formats_cnt = ARRAY_SIZE(channel_formats),
+       .config_max_width = 2048,
+       .config_max_height = 2048,
+       .driver = &ade_driver,
+       .crtc_helper_funcs = &ade_crtc_helper_funcs,
+       .crtc_funcs = &ade_crtc_funcs,
+       .plane_helper_funcs = &ade_plane_helper_funcs,
+       .plane_funcs = &ade_plane_funcs,
+       .mode_config_funcs = &ade_mode_config_funcs,
+
+       .alloc_hw_ctx = ade_hw_ctx_alloc,
+       .cleanup_hw_ctx = ade_hw_ctx_cleanup,
 };
index 204c94c..d3145ae 100644 (file)
 
 #include "kirin_drm_drv.h"
 
-static struct kirin_dc_ops *dc_ops;
+#define KIRIN_MAX_PLANE        2
 
-static int kirin_drm_kms_cleanup(struct drm_device *dev)
+struct kirin_drm_private {
+       struct kirin_crtc crtc;
+       struct kirin_plane planes[KIRIN_MAX_PLANE];
+       void *hw_ctx;
+};
+
+static int kirin_drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+                              struct drm_plane *plane,
+                              const struct kirin_drm_data *driver_data)
 {
-       drm_kms_helper_poll_fini(dev);
-       dc_ops->cleanup(to_platform_device(dev->dev));
-       drm_mode_config_cleanup(dev);
+       struct device_node *port;
+       int ret;
+
+       /* set crtc port so that
+        * drm_of_find_possible_crtcs call works
+        */
+       port = of_get_child_by_name(dev->dev->of_node, "port");
+       if (!port) {
+               DRM_ERROR("no port node found in %pOF\n", dev->dev->of_node);
+               return -EINVAL;
+       }
+       of_node_put(port);
+       crtc->port = port;
+
+       ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+                                       driver_data->crtc_funcs, NULL);
+       if (ret) {
+               DRM_ERROR("failed to init crtc.\n");
+               return ret;
+       }
+
+       drm_crtc_helper_add(crtc, driver_data->crtc_helper_funcs);
 
        return 0;
 }
 
-static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
-       .fb_create = drm_gem_fb_create,
-       .atomic_check = drm_atomic_helper_check,
-       .atomic_commit = drm_atomic_helper_commit,
-};
+static int kirin_drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                               enum drm_plane_type type,
+                               const struct kirin_drm_data *data)
+{
+       int ret = 0;
+
+       ret = drm_universal_plane_init(dev, plane, 1, data->plane_funcs,
+                                      data->channel_formats,
+                                      data->channel_formats_cnt,
+                                      NULL, type, NULL);
+       if (ret) {
+               DRM_ERROR("fail to init plane, ch=%d\n", 0);
+               return ret;
+       }
+
+       drm_plane_helper_add(plane, data->plane_helper_funcs);
 
-static void kirin_drm_mode_config_init(struct drm_device *dev)
+       return 0;
+}
+
+static void kirin_drm_private_cleanup(struct drm_device *dev)
 {
-       dev->mode_config.min_width = 0;
-       dev->mode_config.min_height = 0;
+       struct kirin_drm_private *kirin_priv = dev->dev_private;
+       struct kirin_drm_data *data;
 
-       dev->mode_config.max_width = 2048;
-       dev->mode_config.max_height = 2048;
+       data = (struct kirin_drm_data *)of_device_get_match_data(dev->dev);
+       if (data->cleanup_hw_ctx)
+               data->cleanup_hw_ctx(kirin_priv->hw_ctx);
 
-       dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
+       devm_kfree(dev->dev, kirin_priv);
+       dev->dev_private = NULL;
 }
 
-static int kirin_drm_kms_init(struct drm_device *dev)
+static int kirin_drm_private_init(struct drm_device *dev,
+                                 const struct kirin_drm_data *driver_data)
 {
+       struct platform_device *pdev = to_platform_device(dev->dev);
+       struct kirin_drm_private *kirin_priv;
+       struct drm_plane *prim_plane;
+       enum drm_plane_type type;
+       void *ctx;
        int ret;
+       u32 ch;
+
+       kirin_priv = devm_kzalloc(dev->dev, sizeof(*kirin_priv), GFP_KERNEL);
+       if (!kirin_priv) {
+               DRM_ERROR("failed to alloc kirin_drm_private\n");
+               return -ENOMEM;
+       }
+
+       ctx = driver_data->alloc_hw_ctx(pdev, &kirin_priv->crtc.base);
+       if (IS_ERR(ctx)) {
+               DRM_ERROR("failed to initialize kirin_priv hw ctx\n");
+               return -EINVAL;
+       }
+       kirin_priv->hw_ctx = ctx;
+
+       /*
+        * plane init
+        * TODO: Now only support primary plane, overlay planes
+        * need to do.
+        */
+       for (ch = 0; ch < driver_data->num_planes; ch++) {
+               if (ch == driver_data->prim_plane)
+                       type = DRM_PLANE_TYPE_PRIMARY;
+               else
+                       type = DRM_PLANE_TYPE_OVERLAY;
+               ret = kirin_drm_plane_init(dev, &kirin_priv->planes[ch].base,
+                                          type, driver_data);
+               if (ret)
+                       return ret;
+               kirin_priv->planes[ch].ch = ch;
+               kirin_priv->planes[ch].hw_ctx = ctx;
+       }
 
-       dev_set_drvdata(dev->dev, dev);
+       /* crtc init */
+       prim_plane = &kirin_priv->planes[driver_data->prim_plane].base;
+       ret = kirin_drm_crtc_init(dev, &kirin_priv->crtc.base,
+                                 prim_plane, driver_data);
+       if (ret)
+               return ret;
+       kirin_priv->crtc.hw_ctx = ctx;
+       dev->dev_private = kirin_priv;
+
+       return 0;
+}
+
+static int kirin_drm_kms_init(struct drm_device *dev,
+                             const struct kirin_drm_data *driver_data)
+{
+       int ret;
 
        /* dev->mode_config initialization */
        drm_mode_config_init(dev);
-       kirin_drm_mode_config_init(dev);
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+       dev->mode_config.max_width = driver_data->config_max_width;
+       dev->mode_config.max_height = driver_data->config_max_width;
+       dev->mode_config.funcs = driver_data->mode_config_funcs;
 
        /* display controller init */
-       ret = dc_ops->init(to_platform_device(dev->dev));
+       ret = kirin_drm_private_init(dev, driver_data);
        if (ret)
                goto err_mode_config_cleanup;
 
@@ -76,7 +176,7 @@ static int kirin_drm_kms_init(struct drm_device *dev)
        ret = component_bind_all(dev->dev, dev);
        if (ret) {
                DRM_ERROR("failed to bind all component.\n");
-               goto err_dc_cleanup;
+               goto err_private_cleanup;
        }
 
        /* vblank init */
@@ -98,62 +198,78 @@ static int kirin_drm_kms_init(struct drm_device *dev)
 
 err_unbind_all:
        component_unbind_all(dev->dev, dev);
-err_dc_cleanup:
-       dc_ops->cleanup(to_platform_device(dev->dev));
+err_private_cleanup:
+       kirin_drm_private_cleanup(dev);
 err_mode_config_cleanup:
        drm_mode_config_cleanup(dev);
-
        return ret;
 }
 
-DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops);
-
-static int kirin_gem_cma_dumb_create(struct drm_file *file,
-                                    struct drm_device *dev,
-                                    struct drm_mode_create_dumb *args)
+static int compare_of(struct device *dev, void *data)
 {
-       return drm_gem_cma_dumb_create_internal(file, dev, args);
+       return dev->of_node == data;
 }
 
-static struct drm_driver kirin_drm_driver = {
-       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
-       .fops                   = &kirin_drm_fops,
-
-       .gem_free_object_unlocked = drm_gem_cma_free_object,
-       .gem_vm_ops             = &drm_gem_cma_vm_ops,
-       .dumb_create            = kirin_gem_cma_dumb_create,
-
-       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
-       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
-       .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
-       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
-       .gem_prime_vmap         = drm_gem_cma_prime_vmap,
-       .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
-       .gem_prime_mmap         = drm_gem_cma_prime_mmap,
-
-       .name                   = "kirin",
-       .desc                   = "Hisilicon Kirin SoCs' DRM Driver",
-       .date                   = "20150718",
-       .major                  = 1,
-       .minor                  = 0,
-};
+static int kirin_drm_kms_cleanup(struct drm_device *dev)
+{
+       drm_kms_helper_poll_fini(dev);
+       kirin_drm_private_cleanup(dev);
+       drm_mode_config_cleanup(dev);
 
-static int compare_of(struct device *dev, void *data)
+       return 0;
+}
+
+static int kirin_drm_connectors_register(struct drm_device *dev)
 {
-       return dev->of_node == data;
+       struct drm_connector *connector;
+       struct drm_connector *failed_connector;
+       struct drm_connector_list_iter conn_iter;
+       int ret;
+
+       mutex_lock(&dev->mode_config.mutex);
+       drm_connector_list_iter_begin(dev, &conn_iter);
+       drm_for_each_connector_iter(connector, &conn_iter) {
+               ret = drm_connector_register(connector);
+               if (ret) {
+                       failed_connector = connector;
+                       goto err;
+               }
+       }
+       drm_connector_list_iter_end(&conn_iter);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return 0;
+
+err:
+       drm_connector_list_iter_begin(dev, &conn_iter);
+       drm_for_each_connector_iter(connector, &conn_iter) {
+               if (failed_connector == connector)
+                       break;
+               drm_connector_unregister(connector);
+       }
+       drm_connector_list_iter_end(&conn_iter);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
 }
 
 static int kirin_drm_bind(struct device *dev)
 {
-       struct drm_driver *driver = &kirin_drm_driver;
+       struct kirin_drm_data *driver_data;
        struct drm_device *drm_dev;
        int ret;
 
-       drm_dev = drm_dev_alloc(driver, dev);
+       driver_data = (struct kirin_drm_data *)of_device_get_match_data(dev);
+       if (!driver_data)
+               return -EINVAL;
+
+       drm_dev = drm_dev_alloc(driver_data->driver, dev);
        if (IS_ERR(drm_dev))
                return PTR_ERR(drm_dev);
+       dev_set_drvdata(dev, drm_dev);
 
-       ret = kirin_drm_kms_init(drm_dev);
+       /* display controller init */
+       ret = kirin_drm_kms_init(drm_dev, driver_data);
        if (ret)
                goto err_drm_dev_put;
 
@@ -163,8 +279,17 @@ static int kirin_drm_bind(struct device *dev)
 
        drm_fbdev_generic_setup(drm_dev, 32);
 
+       /* connectors should be registered after drm device register */
+       if (driver_data->register_connects) {
+               ret = kirin_drm_connectors_register(drm_dev);
+               if (ret)
+                       goto err_drm_dev_unregister;
+       }
+
        return 0;
 
+err_drm_dev_unregister:
+       drm_dev_unregister(drm_dev);
 err_kms_cleanup:
        kirin_drm_kms_cleanup(drm_dev);
 err_drm_dev_put:
@@ -194,12 +319,6 @@ static int kirin_drm_platform_probe(struct platform_device *pdev)
        struct component_match *match = NULL;
        struct device_node *remote;
 
-       dc_ops = (struct kirin_dc_ops *)of_device_get_match_data(dev);
-       if (!dc_ops) {
-               DRM_ERROR("failed to get dt id data\n");
-               return -EINVAL;
-       }
-
        remote = of_graph_get_remote_node(np, 0, 0);
        if (!remote)
                return -ENODEV;
@@ -208,20 +327,17 @@ static int kirin_drm_platform_probe(struct platform_device *pdev)
        of_node_put(remote);
 
        return component_master_add_with_match(dev, &kirin_drm_ops, match);
-
-       return 0;
 }
 
 static int kirin_drm_platform_remove(struct platform_device *pdev)
 {
        component_master_del(&pdev->dev, &kirin_drm_ops);
-       dc_ops = NULL;
        return 0;
 }
 
 static const struct of_device_id kirin_drm_dt_ids[] = {
        { .compatible = "hisilicon,hi6220-ade",
-         .data = &ade_dc_ops,
+         .data = &ade_driver_data,
        },
        { /* end node */ },
 };
index 22d1291..4d5c05a 100644 (file)
@@ -7,14 +7,52 @@
 #ifndef __KIRIN_DRM_DRV_H__
 #define __KIRIN_DRM_DRV_H__
 
-#define MAX_CRTC       2
+#define to_kirin_crtc(crtc) \
+       container_of(crtc, struct kirin_crtc, base)
+
+#define to_kirin_plane(plane) \
+       container_of(plane, struct kirin_plane, base)
+
+/* kirin-format translate table */
+struct kirin_format {
+       u32 pixel_format;
+       u32 hw_format;
+};
+
+struct kirin_crtc {
+       struct drm_crtc base;
+       void *hw_ctx;
+       bool enable;
+};
+
+struct kirin_plane {
+       struct drm_plane base;
+       void *hw_ctx;
+       u32 ch;
+};
 
 /* display controller init/cleanup ops */
-struct kirin_dc_ops {
-       int (*init)(struct platform_device *pdev);
-       void (*cleanup)(struct platform_device *pdev);
+struct kirin_drm_data {
+       const u32 *channel_formats;
+       u32 channel_formats_cnt;
+       int config_max_width;
+       int config_max_height;
+       bool register_connects;
+       u32 num_planes;
+       u32 prim_plane;
+
+       struct drm_driver *driver;
+       const struct drm_crtc_helper_funcs *crtc_helper_funcs;
+       const struct drm_crtc_funcs *crtc_funcs;
+       const struct drm_plane_helper_funcs *plane_helper_funcs;
+       const struct drm_plane_funcs  *plane_funcs;
+       const struct drm_mode_config_funcs *mode_config_funcs;
+
+       void *(*alloc_hw_ctx)(struct platform_device *pdev,
+                             struct drm_crtc *crtc);
+       void (*cleanup_hw_ctx)(void *hw_ctx);
 };
 
-extern const struct kirin_dc_ops ade_dc_ops;
+extern struct kirin_drm_data ade_driver_data;
 
 #endif /* __KIRIN_DRM_DRV_H__ */
index 87a38c6..00786a1 100644 (file)
@@ -30,6 +30,7 @@ config DRM_I915_DEBUG
         select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
         select DRM_DEBUG_MM if DRM=y
        select DRM_DEBUG_SELFTEST
+       select DMABUF_SELFTESTS
        select SW_SYNC # signaling validation framework (igt/syncobj*)
        select DRM_I915_SW_FENCE_DEBUG_OBJECTS
        select DRM_I915_SELFTEST
index bba2532..57ae1c1 100644 (file)
@@ -575,7 +575,7 @@ int meson_crtc_create(struct meson_drm *priv)
                return ret;
        }
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                meson_crtc->enable_osd1 = meson_g12a_crtc_enable_osd1;
                meson_crtc->enable_vd1 = meson_g12a_crtc_enable_vd1;
                meson_crtc->viu_offset = MESON_G12A_VIU_OFFSET;
index ae01661..a24f8de 100644 (file)
@@ -209,6 +209,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
        priv->drm = drm;
        priv->dev = dev;
 
+       priv->compat = (enum vpu_compatible)of_device_get_match_data(priv->dev);
+
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
        regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(regs)) {
@@ -453,10 +455,14 @@ static int meson_drv_probe(struct platform_device *pdev)
 };
 
 static const struct of_device_id dt_match[] = {
-       { .compatible = "amlogic,meson-gxbb-vpu" },
-       { .compatible = "amlogic,meson-gxl-vpu" },
-       { .compatible = "amlogic,meson-gxm-vpu" },
-       { .compatible = "amlogic,meson-g12a-vpu" },
+       { .compatible = "amlogic,meson-gxbb-vpu",
+         .data       = (void *)VPU_COMPATIBLE_GXBB },
+       { .compatible = "amlogic,meson-gxl-vpu",
+         .data       = (void *)VPU_COMPATIBLE_GXL },
+       { .compatible = "amlogic,meson-gxm-vpu",
+         .data       = (void *)VPU_COMPATIBLE_GXM },
+       { .compatible = "amlogic,meson-g12a-vpu",
+         .data       = (void *)VPU_COMPATIBLE_G12A },
        {}
 };
 MODULE_DEVICE_TABLE(of, dt_match);
index c9aaec1..820d07b 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/device.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/regmap.h>
 
 struct drm_crtc;
@@ -16,8 +17,16 @@ struct drm_device;
 struct drm_plane;
 struct meson_drm;
 
+enum vpu_compatible {
+       VPU_COMPATIBLE_GXBB = 0,
+       VPU_COMPATIBLE_GXL  = 1,
+       VPU_COMPATIBLE_GXM  = 2,
+       VPU_COMPATIBLE_G12A = 3,
+};
+
 struct meson_drm {
        struct device *dev;
+       enum vpu_compatible compat;
        void __iomem *io_base;
        struct regmap *hhi;
        int vsync_irq;
@@ -116,9 +125,9 @@ struct meson_drm {
 };
 
 static inline int meson_vpu_is_compatible(struct meson_drm *priv,
-                                         const char *compat)
+                                         enum vpu_compatible family)
 {
-       return of_device_is_compatible(priv->dev->of_node, compat);
+       return priv->compat == family;
 }
 
 #endif /* __MESON_DRV_H */
index f893ebd..68bbd98 100644 (file)
@@ -937,7 +937,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
        reset_control_reset(meson_dw_hdmi->hdmitx_phy);
 
        /* Enable APB3 fail on error */
-       if (!meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                writel_bits_relaxed(BIT(15), BIT(15),
                                    meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG);
                writel_bits_relaxed(BIT(15), BIT(15),
index 5aa9dcb..2468b02 100644 (file)
@@ -513,7 +513,7 @@ static void meson_overlay_atomic_disable(struct drm_plane *plane,
        priv->viu.vd1_enabled = false;
 
        /* Disable VD1 */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
                writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
                writel_relaxed(0, priv->io_base + _REG(VD1_IF0_GEN_REG + 0x17b0));
index b9e1e11..ed54322 100644 (file)
@@ -138,7 +138,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
                                      OSD_ENDIANNESS_LE);
 
        /* On GXBB, Use the old non-HDR RGB2YUV converter */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
                priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB;
 
        switch (fb->format->format) {
@@ -292,7 +292,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
        priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1;
        priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1;
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                priv->viu.osd_blend_din0_scope_h = ((dest.x2 - 1) << 16) | dest.x1;
                priv->viu.osd_blend_din0_scope_v = ((dest.y2 - 1) << 16) | dest.y1;
                priv->viu.osb_blend0_size = dst_h << 16 | dst_w;
@@ -308,8 +308,8 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
 
        if (!meson_plane->enabled) {
                /* Reset OSD1 before enabling it on GXL+ SoCs */
-               if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                   meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+               if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                   meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
                        meson_viu_osd1_reset(priv);
 
                meson_plane->enabled = true;
@@ -327,7 +327,7 @@ static void meson_plane_atomic_disable(struct drm_plane *plane,
        struct meson_drm *priv = meson_plane->priv;
 
        /* Disable OSD1 */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                writel_bits_relaxed(VIU_OSD1_POSTBLD_SRC_OSD1, 0,
                                    priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
        else
index 869231c..ac491a7 100644 (file)
@@ -242,7 +242,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
        unsigned int val;
 
        /* Setup PLL to output 1.485GHz */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00404e00);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
@@ -254,8 +254,8 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
                /* Poll for lock bit */
                regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
                                         (val & HDMI_PLL_LOCK), 10, 0);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                  meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                  meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0xa6212844);
@@ -272,7 +272,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
                /* Poll for lock bit */
                regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
                                         (val & HDMI_PLL_LOCK), 10, 0);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x1a0504f7);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00010000);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x00000000);
@@ -300,7 +300,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
                                VCLK2_DIV_MASK, (55 - 1));
 
        /* select vid_pll for vclk2 */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
                                        VCLK2_SEL_MASK, (0 << VCLK2_SEL_SHIFT));
        else
@@ -455,7 +455,7 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
 {
        unsigned int val;
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000200 | m);
                if (frac)
                        regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2,
@@ -475,8 +475,8 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
                /* Poll for lock bit */
                regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
                                         val, (val & HDMI_PLL_LOCK), 10, 0);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                  meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                  meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000200 | m);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000 | frac);
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
@@ -493,7 +493,7 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
                /* Poll for lock bit */
                regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
                                (val & HDMI_PLL_LOCK), 10, 0);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x0b3a0400 | m);
 
                /* Enable and reset */
@@ -545,36 +545,36 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
                } while(1);
        }
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
                                3 << 16, pll_od_to_reg(od1) << 16);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
                                3 << 21, pll_od_to_reg(od1) << 21);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
                                3 << 16, pll_od_to_reg(od1) << 16);
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
                                3 << 22, pll_od_to_reg(od2) << 22);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
                                3 << 23, pll_od_to_reg(od2) << 23);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
                                3 << 18, pll_od_to_reg(od2) << 18);
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
                                3 << 18, pll_od_to_reg(od3) << 18);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
                                3 << 19, pll_od_to_reg(od3) << 19);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
                                3 << 20, pll_od_to_reg(od3) << 20);
 }
@@ -585,7 +585,7 @@ static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
                                         unsigned int pll_freq)
 {
        /* The GXBB PLL has a /2 pre-multiplier */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
                pll_freq /= 2;
 
        return pll_freq / XTAL_FREQ;
@@ -605,12 +605,12 @@ static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
        unsigned int frac;
 
        /* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
                frac_max = HDMI_FRAC_MAX_GXBB;
                parent_freq *= 2;
        }
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                frac_max = HDMI_FRAC_MAX_G12A;
 
        /* We can have a perfect match !*/
@@ -631,15 +631,15 @@ static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
                                           unsigned int m,
                                           unsigned int frac)
 {
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
                /* Empiric supported min/max dividers */
                if (m < 53 || m > 123)
                        return false;
                if (frac >= HDMI_FRAC_MAX_GXBB)
                        return false;
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                  meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu") ||
-                  meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                  meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) ||
+                  meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                /* Empiric supported min/max dividers */
                if (m < 106 || m > 247)
                        return false;
@@ -759,7 +759,7 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
        /* Set HDMI PLL rate */
        if (!od1 && !od2 && !od3) {
                meson_hdmi_pll_generic_set(priv, pll_base_freq);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
                switch (pll_base_freq) {
                case 2970000:
                        m = 0x3d;
@@ -776,8 +776,8 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
                }
 
                meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                  meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                  meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
                switch (pll_base_freq) {
                case 2970000:
                        m = 0x7b;
@@ -794,7 +794,7 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
                }
 
                meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                switch (pll_base_freq) {
                case 2970000:
                        m = 0x7b;
index 679d227..4efd786 100644 (file)
@@ -1759,7 +1759,7 @@ void meson_venc_disable_vsync(struct meson_drm *priv)
 void meson_venc_init(struct meson_drm *priv)
 {
        /* Disable CVBS VDAC */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
                regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 8);
        } else {
index 6dc130a..9ab27ae 100644 (file)
@@ -155,7 +155,7 @@ static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder)
        struct meson_drm *priv = meson_venc_cvbs->priv;
 
        /* Disable CVBS VDAC */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
                regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
        } else {
@@ -174,14 +174,14 @@ static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
        writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0,
                            priv->io_base + _REG(VENC_VDAC_DACSEL0));
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
                regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
                regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-                meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+                meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
                regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
                regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
                regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
        }
index e70cd55..68cf2c2 100644 (file)
@@ -353,10 +353,10 @@ void meson_viu_init(struct meson_drm *priv)
                            priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
 
        /* On GXL/GXM, Use the 10bit HDR conversion matrix */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
-           meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+           meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
                meson_viu_load_matrix(priv);
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
                                               true);
 
@@ -367,7 +367,7 @@ void meson_viu_init(struct meson_drm *priv)
                VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */
                VIU_OSD_FIFO_LIMITS(2);      /* fifo_lim: 2*16=32 */
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                reg |= meson_viu_osd_burst_length_reg(32);
        else
                reg |= meson_viu_osd_burst_length_reg(64);
@@ -394,7 +394,7 @@ void meson_viu_init(struct meson_drm *priv)
        writel_relaxed(0x00FF00C0,
                        priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
 
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                writel_relaxed(VIU_OSD_BLEND_REORDER(0, 1) |
                               VIU_OSD_BLEND_REORDER(1, 0) |
                               VIU_OSD_BLEND_REORDER(2, 0) |
index 1429f3b..1548376 100644 (file)
@@ -91,20 +91,20 @@ static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_drm *priv,
 void meson_vpp_init(struct meson_drm *priv)
 {
        /* set dummy data default YUV black */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
                writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1));
-       else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) {
+       else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) {
                writel_bits_relaxed(0xff << 16, 0xff << 16,
                                    priv->io_base + _REG(VIU_MISC_CTRL1));
                writel_relaxed(VPP_PPS_DUMMY_DATA_MODE,
                               priv->io_base + _REG(VPP_DOLBY_CTRL));
                writel_relaxed(0x1020080,
                                priv->io_base + _REG(VPP_DUMMY_DATA1));
-       } else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                writel_relaxed(0xf, priv->io_base + _REG(DOLBY_PATH_CTRL));
 
        /* Initialize vpu fifo control registers */
-       if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
                writel_relaxed(VPP_OFIFO_SIZE_DEFAULT,
                               priv->io_base + _REG(VPP_OFIFO_SIZE));
        else
@@ -113,7 +113,7 @@ void meson_vpp_init(struct meson_drm *priv)
        writel_relaxed(VPP_POSTBLEND_HOLD_LINES(4) | VPP_PREBLEND_HOLD_LINES(4),
                       priv->io_base + _REG(VPP_HOLD_LINES));
 
-       if (!meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
+       if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
                /* Turn off preblend */
                writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0,
                                    priv->io_base + _REG(VPP_MISC));
index e0b1bbe..e918b43 100644 (file)
@@ -185,31 +185,24 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
        *size = roundup_64(*size, PAGE_SIZE);
 }
 
-int
-nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
-              uint32_t flags, uint32_t tile_mode, uint32_t tile_flags,
-              struct sg_table *sg, struct dma_resv *robj,
-              struct nouveau_bo **pnvbo)
+struct nouveau_bo *
+nouveau_bo_alloc(struct nouveau_cli *cli, u64 size, u32 flags, u32 tile_mode,
+                u32 tile_flags)
 {
        struct nouveau_drm *drm = cli->drm;
        struct nouveau_bo *nvbo;
        struct nvif_mmu *mmu = &cli->mmu;
        struct nvif_vmm *vmm = cli->svm.cli ? &cli->svm.vmm : &cli->vmm.vmm;
-       size_t acc_size;
-       int type = ttm_bo_type_device;
-       int ret, i, pi = -1;
+       int i, pi = -1;
 
        if (!size) {
                NV_WARN(drm, "skipped size %016llx\n", size);
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
 
-       if (sg)
-               type = ttm_bo_type_sg;
-
        nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
        if (!nvbo)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
        INIT_LIST_HEAD(&nvbo->head);
        INIT_LIST_HEAD(&nvbo->entry);
        INIT_LIST_HEAD(&nvbo->vma_list);
@@ -231,7 +224,7 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
                nvbo->kind = (tile_flags & 0x0000ff00) >> 8;
                if (!nvif_mmu_kind_valid(mmu, nvbo->kind)) {
                        kfree(nvbo);
-                       return -EINVAL;
+                       return ERR_PTR(-EINVAL);
                }
 
                nvbo->comp = mmu->kind[nvbo->kind] != nvbo->kind;
@@ -241,7 +234,7 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
                nvbo->comp = (tile_flags & 0x00030000) >> 16;
                if (!nvif_mmu_kind_valid(mmu, nvbo->kind)) {
                        kfree(nvbo);
-                       return -EINVAL;
+                       return ERR_PTR(-EINVAL);
                }
        } else {
                nvbo->zeta = (tile_flags & 0x00000007);
@@ -278,7 +271,7 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
        }
 
        if (WARN_ON(pi < 0))
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        /* Disable compression if suitable settings couldn't be found. */
        if (nvbo->comp && !vmm->page[pi].comp) {
@@ -288,23 +281,51 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
        }
        nvbo->page = vmm->page[pi].shift;
 
+       return nvbo;
+}
+
+int
+nouveau_bo_init(struct nouveau_bo *nvbo, u64 size, int align, u32 flags,
+               struct sg_table *sg, struct dma_resv *robj)
+{
+       int type = sg ? ttm_bo_type_sg : ttm_bo_type_device;
+       size_t acc_size;
+       int ret;
+
+       acc_size = ttm_bo_dma_acc_size(nvbo->bo.bdev, size, sizeof(*nvbo));
+
        nouveau_bo_fixup_align(nvbo, flags, &align, &size);
        nvbo->bo.mem.num_pages = size >> PAGE_SHIFT;
        nouveau_bo_placement_set(nvbo, flags, 0);
 
-       acc_size = ttm_bo_dma_acc_size(&drm->ttm.bdev, size,
-                                      sizeof(struct nouveau_bo));
-
-       ret = ttm_bo_init(&drm->ttm.bdev, &nvbo->bo, size,
-                         type, &nvbo->placement,
-                         align >> PAGE_SHIFT, false, acc_size, sg,
-                         robj, nouveau_bo_del_ttm);
-
+       ret = ttm_bo_init(nvbo->bo.bdev, &nvbo->bo, size, type,
+                         &nvbo->placement, align >> PAGE_SHIFT, false,
+                         acc_size, sg, robj, nouveau_bo_del_ttm);
        if (ret) {
                /* ttm will call nouveau_bo_del_ttm if it fails.. */
                return ret;
        }
 
+       return 0;
+}
+
+int
+nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
+              uint32_t flags, uint32_t tile_mode, uint32_t tile_flags,
+              struct sg_table *sg, struct dma_resv *robj,
+              struct nouveau_bo **pnvbo)
+{
+       struct nouveau_bo *nvbo;
+       int ret;
+
+       nvbo = nouveau_bo_alloc(cli, size, flags, tile_mode, tile_flags);
+       if (IS_ERR(nvbo))
+               return PTR_ERR(nvbo);
+
+       ret = nouveau_bo_init(nvbo, size, align, flags, sg, robj);
+       if (ret)
+               return ret;
+
        *pnvbo = nvbo;
        return 0;
 }
index 3ae8483..62930d8 100644 (file)
@@ -71,6 +71,10 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
 extern struct ttm_bo_driver nouveau_bo_driver;
 
 void nouveau_bo_move_init(struct nouveau_drm *);
+struct nouveau_bo *nouveau_bo_alloc(struct nouveau_cli *, u64 size, u32 flags,
+                                   u32 tile_mode, u32 tile_flags);
+int  nouveau_bo_init(struct nouveau_bo *, u64 size, int align, u32 flags,
+                    struct sg_table *sg, struct dma_resv *robj);
 int  nouveau_bo_new(struct nouveau_cli *, u64 size, int align, u32 flags,
                    u32 tile_mode, u32 tile_flags, struct sg_table *sg,
                    struct dma_resv *robj,
index c77302f..c2bfc05 100644 (file)
@@ -188,11 +188,23 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain,
        if (domain & NOUVEAU_GEM_DOMAIN_COHERENT)
                flags |= TTM_PL_FLAG_UNCACHED;
 
-       ret = nouveau_bo_new(cli, size, align, flags, tile_mode,
-                            tile_flags, NULL, NULL, pnvbo);
-       if (ret)
+       nvbo = nouveau_bo_alloc(cli, size, flags, tile_mode, tile_flags);
+       if (IS_ERR(nvbo))
+               return PTR_ERR(nvbo);
+
+       /* Initialize the embedded gem-object. We return a single gem-reference
+        * to the caller, instead of a normal nouveau_bo ttm reference. */
+       ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, size);
+       if (ret) {
+               nouveau_bo_ref(NULL, &nvbo);
+               return ret;
+       }
+
+       ret = nouveau_bo_init(nvbo, size, align, flags, NULL, NULL);
+       if (ret) {
+               nouveau_bo_ref(NULL, &nvbo);
                return ret;
-       nvbo = *pnvbo;
+       }
 
        /* we restrict allowed domains on nv50+ to only the types
         * that were requested at creation time.  not possibly on
@@ -203,15 +215,8 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain,
        if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA)
                nvbo->valid_domains &= domain;
 
-       /* Initialize the embedded gem-object. We return a single gem-reference
-        * to the caller, instead of a normal nouveau_bo ttm reference. */
-       ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, nvbo->bo.mem.size);
-       if (ret) {
-               nouveau_bo_ref(NULL, pnvbo);
-               return -ENOMEM;
-       }
-
        nvbo->bo.persistent_swap_storage = nvbo->bo.base.filp;
+       *pnvbo = nvbo;
        return 0;
 }
 
index 6a222cd..84658d4 100644 (file)
@@ -62,28 +62,34 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_bo *nvbo;
        struct dma_resv *robj = attach->dmabuf->resv;
+       size_t size = attach->dmabuf->size;
        u32 flags = 0;
        int ret;
 
        flags = TTM_PL_FLAG_TT;
 
        dma_resv_lock(robj, NULL);
-       ret = nouveau_bo_new(&drm->client, attach->dmabuf->size, 0, flags, 0, 0,
-                            sg, robj, &nvbo);
+       nvbo = nouveau_bo_alloc(&drm->client, size, flags, 0, 0);
        dma_resv_unlock(robj);
-       if (ret)
-               return ERR_PTR(ret);
+       if (IS_ERR(nvbo))
+               return ERR_CAST(nvbo);
 
        nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART;
 
        /* Initialize the embedded gem-object. We return a single gem-reference
         * to the caller, instead of a normal nouveau_bo ttm reference. */
-       ret = drm_gem_object_init(dev, &nvbo->bo.base, nvbo->bo.mem.size);
+       ret = drm_gem_object_init(dev, &nvbo->bo.base, size);
        if (ret) {
                nouveau_bo_ref(NULL, &nvbo);
                return ERR_PTR(-ENOMEM);
        }
 
+       ret = nouveau_bo_init(nvbo, size, 0, flags, sg, robj);
+       if (ret) {
+               nouveau_bo_ref(NULL, &nvbo);
+               return ERR_PTR(ret);
+       }
+
        return &nvbo->bo.base;
 }
 
index 5711b7a..e226324 100644 (file)
@@ -923,7 +923,6 @@ dss_debugfs_create_file(struct dss_device *dss, const char *name,
                        void *data)
 {
        struct dss_debugfs_entry *entry;
-       struct dentry *d;
 
        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
        if (!entry)
@@ -931,15 +930,9 @@ dss_debugfs_create_file(struct dss_device *dss, const char *name,
 
        entry->show_fn = show_fn;
        entry->data = data;
+       entry->dentry = debugfs_create_file(name, 0444, dss->debugfs.root,
+                                           entry, &dss_debug_fops);
 
-       d = debugfs_create_file(name, 0444, dss->debugfs.root, entry,
-                               &dss_debug_fops);
-       if (IS_ERR(d)) {
-               kfree(entry);
-               return ERR_CAST(d);
-       }
-
-       entry->dentry = d;
        return entry;
 }
 
index 84e1be9..73ec998 100644 (file)
@@ -53,8 +53,12 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
        memset(&info, 0, sizeof(info));
        info.rotation_type = OMAP_DSS_ROT_NONE;
        info.rotation = DRM_MODE_ROTATE_0;
-       info.global_alpha = 0xff;
+       info.global_alpha = state->alpha >> 8;
        info.zorder = state->normalized_zpos;
+       if (state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
+               info.pre_mult_alpha = 1;
+       else
+               info.pre_mult_alpha = 0;
 
        /* update scanout: */
        omap_framebuffer_update_scanout(state->fb, state, &info);
@@ -285,6 +289,9 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
 
        omap_plane_install_properties(plane, &plane->base);
        drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1);
+       drm_plane_create_alpha_property(plane);
+       drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) |
+                                            BIT(DRM_MODE_BLEND_COVERAGE));
 
        return plane;
 
index e7727b2..536a0d4 100644 (file)
@@ -6,10 +6,6 @@
   - Bifrost specific feature and issue handling
   - Coherent DMA support
 
-- Per FD address space support. The h/w supports multiple addresses spaces.
-  The hard part is handling when more address spaces are needed than what
-  the h/w provides.
-
 - Support userspace controlled GPU virtual addresses. Needed for Vulkan. (Tomeu)
 
 - Compute job support. So called 'compute only' jobs need to be plumbed up to
index a7c18bc..710d903 100644 (file)
@@ -39,7 +39,7 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
         * If frequency scaling from low to high, adjust voltage first.
         * If frequency scaling from high to low, adjust frequency first.
         */
-       if (old_clk_rate < target_rate) {
+       if (old_clk_rate < target_rate && pfdev->regulator) {
                err = regulator_set_voltage(pfdev->regulator, target_volt,
                                            target_volt);
                if (err) {
@@ -58,7 +58,7 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
                return err;
        }
 
-       if (old_clk_rate > target_rate) {
+       if (old_clk_rate > target_rate && pfdev->regulator) {
                err = regulator_set_voltage(pfdev->regulator, target_volt,
                                            target_volt);
                if (err)
@@ -136,9 +136,6 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
        int ret;
        struct dev_pm_opp *opp;
 
-       if (!pfdev->regulator)
-               return 0;
-
        ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev);
        if (ret == -ENODEV) /* Optional, continue without devfreq */
                return 0;
@@ -163,12 +160,18 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
                DRM_DEV_ERROR(&pfdev->pdev->dev, "Couldn't initialize GPU devfreq\n");
                ret = PTR_ERR(pfdev->devfreq.devfreq);
                pfdev->devfreq.devfreq = NULL;
+               dev_pm_opp_of_remove_table(&pfdev->pdev->dev);
                return ret;
        }
 
        return 0;
 }
 
+void panfrost_devfreq_fini(struct panfrost_device *pfdev)
+{
+       dev_pm_opp_of_remove_table(&pfdev->pdev->dev);
+}
+
 void panfrost_devfreq_resume(struct panfrost_device *pfdev)
 {
        int i;
index eb99953..e3bc63e 100644 (file)
@@ -5,6 +5,7 @@
 #define __PANFROST_DEVFREQ_H__
 
 int panfrost_devfreq_init(struct panfrost_device *pfdev);
+void panfrost_devfreq_fini(struct panfrost_device *pfdev);
 
 void panfrost_devfreq_resume(struct panfrost_device *pfdev);
 void panfrost_devfreq_suspend(struct panfrost_device *pfdev);
index 9814f4c..4da71bb 100644 (file)
@@ -123,8 +123,10 @@ int panfrost_device_init(struct panfrost_device *pfdev)
        mutex_init(&pfdev->sched_lock);
        mutex_init(&pfdev->reset_lock);
        INIT_LIST_HEAD(&pfdev->scheduled_jobs);
+       INIT_LIST_HEAD(&pfdev->as_lru_list);
 
        spin_lock_init(&pfdev->hwaccess_lock);
+       spin_lock_init(&pfdev->as_lock);
 
        err = panfrost_clk_init(pfdev);
        if (err) {
index 4e5641d..f503c56 100644 (file)
@@ -5,6 +5,8 @@
 #ifndef __PANFROST_DEVICE_H__
 #define __PANFROST_DEVICE_H__
 
+#include <linux/atomic.h>
+#include <linux/io-pgtable.h>
 #include <linux/spinlock.h>
 #include <drm/drm_device.h>
 #include <drm/drm_mm.h>
@@ -63,9 +65,6 @@ struct panfrost_device {
 
        spinlock_t hwaccess_lock;
 
-       struct drm_mm mm;
-       spinlock_t mm_lock;
-
        void __iomem *iomem;
        struct clk *clock;
        struct clk *bus_clock;
@@ -74,7 +73,11 @@ struct panfrost_device {
 
        struct panfrost_features features;
 
-       struct panfrost_mmu *mmu;
+       spinlock_t as_lock;
+       unsigned long as_in_use_mask;
+       unsigned long as_alloc_mask;
+       struct list_head as_lru_list;
+
        struct panfrost_job_slot *js;
 
        struct panfrost_job *jobs[NUM_JOB_SLOTS];
@@ -98,10 +101,23 @@ struct panfrost_device {
        } devfreq;
 };
 
+struct panfrost_mmu {
+       struct io_pgtable_cfg pgtbl_cfg;
+       struct io_pgtable_ops *pgtbl_ops;
+       struct mutex lock;
+       int as;
+       atomic_t as_count;
+       struct list_head list;
+};
+
 struct panfrost_file_priv {
        struct panfrost_device *pfdev;
 
        struct drm_sched_entity sched_entity[NUM_JOB_SLOTS];
+
+       struct panfrost_mmu mmu;
+       struct drm_mm mm;
+       spinlock_t mm_lock;
 };
 
 static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev)
index b417546..44a558c 100644 (file)
@@ -403,6 +403,7 @@ static void panfrost_drm_mm_color_adjust(const struct drm_mm_node *node,
 static int
 panfrost_open(struct drm_device *dev, struct drm_file *file)
 {
+       int ret;
        struct panfrost_device *pfdev = dev->dev_private;
        struct panfrost_file_priv *panfrost_priv;
 
@@ -413,7 +414,28 @@ panfrost_open(struct drm_device *dev, struct drm_file *file)
        panfrost_priv->pfdev = pfdev;
        file->driver_priv = panfrost_priv;
 
-       return panfrost_job_open(panfrost_priv);
+       spin_lock_init(&panfrost_priv->mm_lock);
+
+       /* 4G enough for now. can be 48-bit */
+       drm_mm_init(&panfrost_priv->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT);
+       panfrost_priv->mm.color_adjust = panfrost_drm_mm_color_adjust;
+
+       ret = panfrost_mmu_pgtable_alloc(panfrost_priv);
+       if (ret)
+               goto err_pgtable;
+
+       ret = panfrost_job_open(panfrost_priv);
+       if (ret)
+               goto err_job;
+
+       return 0;
+
+err_job:
+       panfrost_mmu_pgtable_free(panfrost_priv);
+err_pgtable:
+       drm_mm_takedown(&panfrost_priv->mm);
+       kfree(panfrost_priv);
+       return ret;
 }
 
 static void
@@ -424,6 +446,8 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file)
        panfrost_perfcnt_close(panfrost_priv);
        panfrost_job_close(panfrost_priv);
 
+       panfrost_mmu_pgtable_free(panfrost_priv);
+       drm_mm_takedown(&panfrost_priv->mm);
        kfree(panfrost_priv);
 }
 
@@ -496,14 +520,9 @@ static int panfrost_probe(struct platform_device *pdev)
        ddev->dev_private = pfdev;
        pfdev->ddev = ddev;
 
-       spin_lock_init(&pfdev->mm_lock);
        mutex_init(&pfdev->shrinker_lock);
        INIT_LIST_HEAD(&pfdev->shrinker_list);
 
-       /* 4G enough for now. can be 48-bit */
-       drm_mm_init(&pfdev->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT);
-       pfdev->mm.color_adjust = panfrost_drm_mm_color_adjust;
-
        pm_runtime_use_autosuspend(pfdev->dev);
        pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */
        pm_runtime_enable(pfdev->dev);
@@ -528,12 +547,14 @@ static int panfrost_probe(struct platform_device *pdev)
         */
        err = drm_dev_register(ddev, 0);
        if (err < 0)
-               goto err_out1;
+               goto err_out2;
 
        panfrost_gem_shrinker_init(ddev);
 
        return 0;
 
+err_out2:
+       panfrost_devfreq_fini(pfdev);
 err_out1:
        panfrost_device_fini(pfdev);
 err_out0:
@@ -552,6 +573,7 @@ static int panfrost_remove(struct platform_device *pdev)
        pm_runtime_get_sync(pfdev->dev);
        pm_runtime_put_sync_autosuspend(pfdev->dev);
        pm_runtime_disable(pfdev->dev);
+       panfrost_devfreq_fini(pfdev);
        panfrost_device_fini(pfdev);
        drm_dev_put(ddev);
        return 0;
index e71f27c..acb07fe 100644 (file)
@@ -47,8 +47,8 @@ static int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_p
        size_t size = obj->size;
        u64 align;
        struct panfrost_gem_object *bo = to_panfrost_bo(obj);
-       struct panfrost_device *pfdev = obj->dev->dev_private;
        unsigned long color = bo->noexec ? PANFROST_BO_NOEXEC : 0;
+       struct panfrost_file_priv *priv = file_priv->driver_priv;
 
        /*
         * Executable buffers cannot cross a 16MB boundary as the program
@@ -61,34 +61,37 @@ static int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_p
        else
                align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
 
-       spin_lock(&pfdev->mm_lock);
-       ret = drm_mm_insert_node_generic(&pfdev->mm, &bo->node,
+       bo->mmu = &priv->mmu;
+       spin_lock(&priv->mm_lock);
+       ret = drm_mm_insert_node_generic(&priv->mm, &bo->node,
                                         size >> PAGE_SHIFT, align, color, 0);
+       spin_unlock(&priv->mm_lock);
        if (ret)
-               goto out;
+               return ret;
 
        if (!bo->is_heap) {
                ret = panfrost_mmu_map(bo);
-               if (ret)
+               if (ret) {
+                       spin_lock(&priv->mm_lock);
                        drm_mm_remove_node(&bo->node);
+                       spin_unlock(&priv->mm_lock);
+               }
        }
-out:
-       spin_unlock(&pfdev->mm_lock);
        return ret;
 }
 
 static void panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv)
 {
        struct panfrost_gem_object *bo = to_panfrost_bo(obj);
-       struct panfrost_device *pfdev = obj->dev->dev_private;
+       struct panfrost_file_priv *priv = file_priv->driver_priv;
 
        if (bo->is_mapped)
                panfrost_mmu_unmap(bo);
 
-       spin_lock(&pfdev->mm_lock);
+       spin_lock(&priv->mm_lock);
        if (drm_mm_node_allocated(&bo->node))
                drm_mm_remove_node(&bo->node);
-       spin_unlock(&pfdev->mm_lock);
+       spin_unlock(&priv->mm_lock);
 }
 
 static int panfrost_gem_pin(struct drm_gem_object *obj)
index e10f583..5092081 100644 (file)
@@ -7,10 +7,13 @@
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_mm.h>
 
+struct panfrost_mmu;
+
 struct panfrost_gem_object {
        struct drm_gem_shmem_object base;
        struct sg_table *sgts;
 
+       struct panfrost_mmu *mmu;
        struct drm_mm_node node;
        bool is_mapped          :1;
        bool noexec             :1;
index 0fc4539..05c85f4 100644 (file)
@@ -153,6 +153,8 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
        if (WARN_ON(job_read(pfdev, JS_COMMAND_NEXT(js))))
                goto end;
 
+       cfg = panfrost_mmu_as_get(pfdev, &job->file_priv->mmu);
+
        panfrost_devfreq_record_transition(pfdev, js);
        spin_lock_irqsave(&pfdev->hwaccess_lock, flags);
 
@@ -163,8 +165,7 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
 
        /* start MMU, medium priority, cache clean/flush on end, clean/flush on
         * start */
-       /* TODO: different address spaces */
-       cfg = JS_CONFIG_THREAD_PRI(8) |
+       cfg |= JS_CONFIG_THREAD_PRI(8) |
                JS_CONFIG_START_FLUSH_CLEAN_INVALIDATE |
                JS_CONFIG_END_FLUSH_CLEAN_INVALIDATE;
 
@@ -377,8 +378,9 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job)
        if (dma_fence_is_signaled(job->done_fence))
                return;
 
-       dev_err(pfdev->dev, "gpu sched timeout, js=%d, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p",
+       dev_err(pfdev->dev, "gpu sched timeout, js=%d, config=0x%x, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p",
                js,
+               job_read(pfdev, JS_CONFIG(js)),
                job_read(pfdev, JS_STATUS(js)),
                job_read(pfdev, JS_HEAD_LO(js)),
                job_read(pfdev, JS_TAIL_LO(js)),
@@ -448,8 +450,12 @@ static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
                }
 
                if (status & JOB_INT_MASK_DONE(j)) {
+                       struct panfrost_job *job = pfdev->jobs[j];
+
+                       pfdev->jobs[j] = NULL;
+                       panfrost_mmu_as_put(pfdev, &job->file_priv->mmu);
                        panfrost_devfreq_record_transition(pfdev, j);
-                       dma_fence_signal(pfdev->jobs[j]->done_fence);
+                       dma_fence_signal(job->done_fence);
                }
 
                status &= ~mask;
index 2ed411f..842bdd7 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier:    GPL-2.0
 /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
+#include <linux/atomic.h>
 #include <linux/bitfield.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #define mmu_write(dev, reg, data) writel(data, dev->iomem + reg)
 #define mmu_read(dev, reg) readl(dev->iomem + reg)
 
-struct panfrost_mmu {
-       struct io_pgtable_cfg pgtbl_cfg;
-       struct io_pgtable_ops *pgtbl_ops;
-       struct mutex lock;
-};
-
 static int wait_ready(struct panfrost_device *pfdev, u32 as_nr)
 {
        int ret;
@@ -85,13 +80,19 @@ static void lock_region(struct panfrost_device *pfdev, u32 as_nr,
 }
 
 
-static int mmu_hw_do_operation(struct panfrost_device *pfdev, u32 as_nr,
-               u64 iova, size_t size, u32 op)
+static int mmu_hw_do_operation(struct panfrost_device *pfdev,
+                              struct panfrost_mmu *mmu,
+                              u64 iova, size_t size, u32 op)
 {
-       unsigned long flags;
-       int ret;
+       int ret, as_nr;
 
-       spin_lock_irqsave(&pfdev->hwaccess_lock, flags);
+       spin_lock(&pfdev->as_lock);
+       as_nr = mmu->as;
+
+       if (as_nr < 0) {
+               spin_unlock(&pfdev->as_lock);
+               return 0;
+       }
 
        if (op != AS_COMMAND_UNLOCK)
                lock_region(pfdev, as_nr, iova, size);
@@ -102,14 +103,15 @@ static int mmu_hw_do_operation(struct panfrost_device *pfdev, u32 as_nr,
        /* Wait for the flush to complete */
        ret = wait_ready(pfdev, as_nr);
 
-       spin_unlock_irqrestore(&pfdev->hwaccess_lock, flags);
+       spin_unlock(&pfdev->as_lock);
 
        return ret;
 }
 
-static void panfrost_mmu_enable(struct panfrost_device *pfdev, u32 as_nr)
+static void panfrost_mmu_enable(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
 {
-       struct io_pgtable_cfg *cfg = &pfdev->mmu->pgtbl_cfg;
+       int as_nr = mmu->as;
+       struct io_pgtable_cfg *cfg = &mmu->pgtbl_cfg;
        u64 transtab = cfg->arm_mali_lpae_cfg.transtab;
        u64 memattr = cfg->arm_mali_lpae_cfg.memattr;
 
@@ -136,9 +138,75 @@ static void mmu_disable(struct panfrost_device *pfdev, u32 as_nr)
        write_cmd(pfdev, as_nr, AS_COMMAND_UPDATE);
 }
 
+u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
+{
+       int as;
+
+       spin_lock(&pfdev->as_lock);
+
+       as = mmu->as;
+       if (as >= 0) {
+               int en = atomic_inc_return(&mmu->as_count);
+               WARN_ON(en >= NUM_JOB_SLOTS);
+
+               list_move(&mmu->list, &pfdev->as_lru_list);
+               goto out;
+       }
+
+       /* Check for a free AS */
+       as = ffz(pfdev->as_alloc_mask);
+       if (!(BIT(as) & pfdev->features.as_present)) {
+               struct panfrost_mmu *lru_mmu;
+
+               list_for_each_entry_reverse(lru_mmu, &pfdev->as_lru_list, list) {
+                       if (!atomic_read(&lru_mmu->as_count))
+                               break;
+               }
+               WARN_ON(&lru_mmu->list == &pfdev->as_lru_list);
+
+               list_del_init(&lru_mmu->list);
+               as = lru_mmu->as;
+
+               WARN_ON(as < 0);
+               lru_mmu->as = -1;
+       }
+
+       /* Assign the free or reclaimed AS to the FD */
+       mmu->as = as;
+       set_bit(as, &pfdev->as_alloc_mask);
+       atomic_set(&mmu->as_count, 1);
+       list_add(&mmu->list, &pfdev->as_lru_list);
+
+       dev_dbg(pfdev->dev, "Assigned AS%d to mmu %p, alloc_mask=%lx", as, mmu, pfdev->as_alloc_mask);
+
+       panfrost_mmu_enable(pfdev, mmu);
+
+out:
+       spin_unlock(&pfdev->as_lock);
+       return as;
+}
+
+void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
+{
+       atomic_dec(&mmu->as_count);
+       WARN_ON(atomic_read(&mmu->as_count) < 0);
+}
+
 void panfrost_mmu_reset(struct panfrost_device *pfdev)
 {
-       panfrost_mmu_enable(pfdev, 0);
+       struct panfrost_mmu *mmu, *mmu_tmp;
+
+       spin_lock(&pfdev->as_lock);
+
+       pfdev->as_alloc_mask = 0;
+
+       list_for_each_entry_safe(mmu, mmu_tmp, &pfdev->as_lru_list, list) {
+               mmu->as = -1;
+               atomic_set(&mmu->as_count, 0);
+               list_del_init(&mmu->list);
+       }
+
+       spin_unlock(&pfdev->as_lock);
 
        mmu_write(pfdev, MMU_INT_CLEAR, ~0);
        mmu_write(pfdev, MMU_INT_MASK, ~0);
@@ -152,21 +220,21 @@ static size_t get_pgsize(u64 addr, size_t size)
        return SZ_2M;
 }
 
-static int mmu_map_sg(struct panfrost_device *pfdev, u64 iova,
-                     int prot, struct sg_table *sgt)
+static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
+                     u64 iova, int prot, struct sg_table *sgt)
 {
        unsigned int count;
        struct scatterlist *sgl;
-       struct io_pgtable_ops *ops = pfdev->mmu->pgtbl_ops;
+       struct io_pgtable_ops *ops = mmu->pgtbl_ops;
        u64 start_iova = iova;
 
-       mutex_lock(&pfdev->mmu->lock);
+       mutex_lock(&mmu->lock);
 
        for_each_sg(sgt->sgl, sgl, sgt->nents, count) {
                unsigned long paddr = sg_dma_address(sgl);
                size_t len = sg_dma_len(sgl);
 
-               dev_dbg(pfdev->dev, "map: iova=%llx, paddr=%lx, len=%zx", iova, paddr, len);
+               dev_dbg(pfdev->dev, "map: as=%d, iova=%llx, paddr=%lx, len=%zx", mmu->as, iova, paddr, len);
 
                while (len) {
                        size_t pgsize = get_pgsize(iova | paddr, len);
@@ -178,10 +246,10 @@ static int mmu_map_sg(struct panfrost_device *pfdev, u64 iova,
                }
        }
 
-       mmu_hw_do_operation(pfdev, 0, start_iova, iova - start_iova,
+       mmu_hw_do_operation(pfdev, mmu, start_iova, iova - start_iova,
                            AS_COMMAND_FLUSH_PT);
 
-       mutex_unlock(&pfdev->mmu->lock);
+       mutex_unlock(&mmu->lock);
 
        return 0;
 }
@@ -208,7 +276,7 @@ int panfrost_mmu_map(struct panfrost_gem_object *bo)
        if (ret < 0)
                return ret;
 
-       mmu_map_sg(pfdev, bo->node.start << PAGE_SHIFT, prot, sgt);
+       mmu_map_sg(pfdev, bo->mmu, bo->node.start << PAGE_SHIFT, prot, sgt);
 
        pm_runtime_mark_last_busy(pfdev->dev);
        pm_runtime_put_autosuspend(pfdev->dev);
@@ -221,7 +289,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo)
 {
        struct drm_gem_object *obj = &bo->base.base;
        struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
-       struct io_pgtable_ops *ops = pfdev->mmu->pgtbl_ops;
+       struct io_pgtable_ops *ops = bo->mmu->pgtbl_ops;
        u64 iova = bo->node.start << PAGE_SHIFT;
        size_t len = bo->node.size << PAGE_SHIFT;
        size_t unmapped_len = 0;
@@ -230,13 +298,13 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo)
        if (WARN_ON(!bo->is_mapped))
                return;
 
-       dev_dbg(pfdev->dev, "unmap: iova=%llx, len=%zx", iova, len);
+       dev_dbg(pfdev->dev, "unmap: as=%d, iova=%llx, len=%zx", bo->mmu->as, iova, len);
 
        ret = pm_runtime_get_sync(pfdev->dev);
        if (ret < 0)
                return;
 
-       mutex_lock(&pfdev->mmu->lock);
+       mutex_lock(&bo->mmu->lock);
 
        while (unmapped_len < len) {
                size_t unmapped_page;
@@ -250,10 +318,10 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo)
                unmapped_len += pgsize;
        }
 
-       mmu_hw_do_operation(pfdev, 0, bo->node.start << PAGE_SHIFT,
+       mmu_hw_do_operation(pfdev, bo->mmu, bo->node.start << PAGE_SHIFT,
                            bo->node.size << PAGE_SHIFT, AS_COMMAND_FLUSH_PT);
 
-       mutex_unlock(&pfdev->mmu->lock);
+       mutex_unlock(&bo->mmu->lock);
 
        pm_runtime_mark_last_busy(pfdev->dev);
        pm_runtime_put_autosuspend(pfdev->dev);
@@ -262,9 +330,9 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo)
 
 static void mmu_tlb_inv_context_s1(void *cookie)
 {
-       struct panfrost_device *pfdev = cookie;
+       struct panfrost_file_priv *priv = cookie;
 
-       mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM);
+       mmu_hw_do_operation(priv->pfdev, &priv->mmu, 0, ~0UL, AS_COMMAND_FLUSH_MEM);
 }
 
 static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
@@ -283,16 +351,69 @@ static const struct iommu_gather_ops mmu_tlb_ops = {
        .tlb_sync       = mmu_tlb_sync_context,
 };
 
+int panfrost_mmu_pgtable_alloc(struct panfrost_file_priv *priv)
+{
+       struct panfrost_mmu *mmu = &priv->mmu;
+       struct panfrost_device *pfdev = priv->pfdev;
+
+       mutex_init(&mmu->lock);
+       INIT_LIST_HEAD(&mmu->list);
+       mmu->as = -1;
+
+       mmu->pgtbl_cfg = (struct io_pgtable_cfg) {
+               .pgsize_bitmap  = SZ_4K | SZ_2M,
+               .ias            = FIELD_GET(0xff, pfdev->features.mmu_features),
+               .oas            = FIELD_GET(0xff00, pfdev->features.mmu_features),
+               .tlb            = &mmu_tlb_ops,
+               .iommu_dev      = pfdev->dev,
+       };
+
+       mmu->pgtbl_ops = alloc_io_pgtable_ops(ARM_MALI_LPAE, &mmu->pgtbl_cfg,
+                                             priv);
+       if (!mmu->pgtbl_ops)
+               return -EINVAL;
+
+       return 0;
+}
+
+void panfrost_mmu_pgtable_free(struct panfrost_file_priv *priv)
+{
+       struct panfrost_device *pfdev = priv->pfdev;
+       struct panfrost_mmu *mmu = &priv->mmu;
+
+       spin_lock(&pfdev->as_lock);
+       if (mmu->as >= 0) {
+               clear_bit(mmu->as, &pfdev->as_alloc_mask);
+               clear_bit(mmu->as, &pfdev->as_in_use_mask);
+               list_del(&mmu->list);
+       }
+       spin_unlock(&pfdev->as_lock);
+
+       free_io_pgtable_ops(mmu->pgtbl_ops);
+}
+
 static struct drm_mm_node *addr_to_drm_mm_node(struct panfrost_device *pfdev, int as, u64 addr)
 {
-       struct drm_mm_node *node;
+       struct drm_mm_node *node = NULL;
        u64 offset = addr >> PAGE_SHIFT;
+       struct panfrost_mmu *mmu;
+
+       spin_lock(&pfdev->as_lock);
+       list_for_each_entry(mmu, &pfdev->as_lru_list, list) {
+               struct panfrost_file_priv *priv;
+               if (as != mmu->as)
+                       continue;
 
-       drm_mm_for_each_node(node, &pfdev->mm) {
-               if (offset >= node->start && offset < (node->start + node->size))
-                       return node;
+               priv = container_of(mmu, struct panfrost_file_priv, mmu);
+               drm_mm_for_each_node(node, &priv->mm) {
+                       if (offset >= node->start && offset < (node->start + node->size))
+                               goto out;
+               }
        }
-       return NULL;
+
+out:
+       spin_unlock(&pfdev->as_lock);
+       return node;
 }
 
 #define NUM_FAULT_PAGES (SZ_2M / PAGE_SIZE)
@@ -317,6 +438,8 @@ int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr)
                         node->start << PAGE_SHIFT);
                return -EINVAL;
        }
+       WARN_ON(bo->mmu->as != as);
+
        /* Assume 2MB alignment and size multiple */
        addr &= ~((u64)SZ_2M - 1);
        page_offset = addr >> PAGE_SHIFT;
@@ -327,14 +450,17 @@ int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr)
        if (!bo->base.pages) {
                bo->sgts = kvmalloc_array(bo->base.base.size / SZ_2M,
                                     sizeof(struct sg_table), GFP_KERNEL | __GFP_ZERO);
-               if (!bo->sgts)
+               if (!bo->sgts) {
+                       mutex_unlock(&bo->base.pages_lock);
                        return -ENOMEM;
+               }
 
                pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT,
                                       sizeof(struct page *), GFP_KERNEL | __GFP_ZERO);
                if (!pages) {
                        kfree(bo->sgts);
                        bo->sgts = NULL;
+                       mutex_unlock(&bo->base.pages_lock);
                        return -ENOMEM;
                }
                bo->base.pages = pages;
@@ -367,11 +493,11 @@ int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr)
                goto err_map;
        }
 
-       mmu_map_sg(pfdev, addr, IOMMU_WRITE | IOMMU_READ | IOMMU_NOEXEC, sgt);
+       mmu_map_sg(pfdev, bo->mmu, addr, IOMMU_WRITE | IOMMU_READ | IOMMU_NOEXEC, sgt);
 
        bo->is_mapped = true;
 
-       dev_dbg(pfdev->dev, "mapped page fault @ %llx", addr);
+       dev_dbg(pfdev->dev, "mapped page fault @ AS%d %llx", as, addr);
 
        return 0;
 
@@ -480,15 +606,8 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
 
 int panfrost_mmu_init(struct panfrost_device *pfdev)
 {
-       struct io_pgtable_ops *pgtbl_ops;
        int err, irq;
 
-       pfdev->mmu = devm_kzalloc(pfdev->dev, sizeof(*pfdev->mmu), GFP_KERNEL);
-       if (!pfdev->mmu)
-               return -ENOMEM;
-
-       mutex_init(&pfdev->mmu->lock);
-
        irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
        if (irq <= 0)
                return -ENODEV;
@@ -501,22 +620,6 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
                dev_err(pfdev->dev, "failed to request mmu irq");
                return err;
        }
-       pfdev->mmu->pgtbl_cfg = (struct io_pgtable_cfg) {
-               .pgsize_bitmap  = SZ_4K | SZ_2M,
-               .ias            = FIELD_GET(0xff, pfdev->features.mmu_features),
-               .oas            = FIELD_GET(0xff00, pfdev->features.mmu_features),
-               .tlb            = &mmu_tlb_ops,
-               .iommu_dev      = pfdev->dev,
-       };
-
-       pgtbl_ops = alloc_io_pgtable_ops(ARM_MALI_LPAE, &pfdev->mmu->pgtbl_cfg,
-                                        pfdev);
-       if (!pgtbl_ops)
-               return -ENOMEM;
-
-       pfdev->mmu->pgtbl_ops = pgtbl_ops;
-
-       panfrost_mmu_enable(pfdev, 0);
 
        return 0;
 }
@@ -525,6 +628,4 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev)
 {
        mmu_write(pfdev, MMU_INT_MASK, 0);
        mmu_disable(pfdev, 0);
-
-       free_io_pgtable_ops(pfdev->mmu->pgtbl_ops);
 }
index d5f9b24..7c5b677 100644 (file)
@@ -5,6 +5,8 @@
 #define __PANFROST_MMU_H__
 
 struct panfrost_gem_object;
+struct panfrost_file_priv;
+struct panfrost_mmu;
 
 int panfrost_mmu_map(struct panfrost_gem_object *bo);
 void panfrost_mmu_unmap(struct panfrost_gem_object *bo);
@@ -13,4 +15,10 @@ int panfrost_mmu_init(struct panfrost_device *pfdev);
 void panfrost_mmu_fini(struct panfrost_device *pfdev);
 void panfrost_mmu_reset(struct panfrost_device *pfdev);
 
+u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
+void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
+
+int panfrost_mmu_pgtable_alloc(struct panfrost_file_priv *priv);
+void panfrost_mmu_pgtable_free(struct panfrost_file_priv *priv);
+
 #endif
index 8504e19..fc1e45d 100644 (file)
@@ -487,11 +487,6 @@ static int aty128_encode_var(struct fb_var_screeninfo *var,
                              const struct aty128fb_par *par);
 static int aty128_decode_var(struct fb_var_screeninfo *var,
                              struct aty128fb_par *par);
-#if 0
-static void aty128_get_pllinfo(struct aty128fb_par *par, void __iomem *bios);
-static void __iomem *aty128_map_ROM(struct pci_dev *pdev,
-                                   const struct aty128fb_par *par);
-#endif
 static void aty128_timings(struct aty128fb_par *par);
 static void aty128_init_engine(struct aty128fb_par *par);
 static void aty128_reset_engine(const struct aty128fb_par *par);
@@ -1665,19 +1660,6 @@ static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
                          struct aty128fb_par *par)
 {
        if (par->chip_gen == rage_M3) {
-#if 0
-               /* Note: For now, on M3, we set palette on both heads, which may
-                * be useless. Can someone with a M3 check this ?
-                * 
-                * This code would still be useful if using the second CRTC to 
-                * do mirroring
-                */
-
-               aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) |
-                           DAC_PALETTE_ACCESS_CNTL);
-               aty_st_8(PALETTE_INDEX, regno);
-               aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
-#endif
                aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
                            ~DAC_PALETTE_ACCESS_CNTL);
        }
index 72bcfbe..6dda5d8 100644 (file)
@@ -1188,19 +1188,6 @@ static int aty_crtc_to_var(const struct crtc *crtc,
                (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
 
        switch (pix_width) {
-#if 0
-       case CRTC_PIX_WIDTH_4BPP:
-               bpp = 4;
-               var->red.offset = 0;
-               var->red.length = 8;
-               var->green.offset = 0;
-               var->green.length = 8;
-               var->blue.offset = 0;
-               var->blue.length = 8;
-               var->transp.offset = 0;
-               var->transp.length = 0;
-               break;
-#endif
        case CRTC_PIX_WIDTH_8BPP:
                bpp = 8;
                var->red.offset = 0;
@@ -1466,11 +1453,6 @@ static int atyfb_set_par(struct fb_info *info)
                var->bits_per_pixel,
                par->crtc.vxres * var->bits_per_pixel / 8);
 #endif /* CONFIG_BOOTX_TEXT */
-#if 0
-       /* switch to accelerator mode */
-       if (!(par->crtc.gen_cntl & CRTC_EXT_DISP_EN))
-               aty_st_le32(CRTC_GEN_CNTL, par->crtc.gen_cntl | CRTC_EXT_DISP_EN, par);
-#endif
 #ifdef DEBUG
 {
        /* dump non shadow CRTC, pll, LCD registers */
@@ -2396,17 +2378,6 @@ static int aty_init(struct fb_info *info)
                        par->pll_ops = &aty_pll_ibm514;
                        break;
 #endif
-#if 0 /* dead code */
-               case CLK_STG1703:
-                       par->pll_ops = &aty_pll_stg1703;
-                       break;
-               case CLK_CH8398:
-                       par->pll_ops = &aty_pll_ch8398;
-                       break;
-               case CLK_ATT20C408:
-                       par->pll_ops = &aty_pll_att20c408;
-                       break;
-#endif
                default:
                        PRINTKI("aty_init: CLK type not implemented yet!");
                        par->pll_ops = &aty_pll_unsupported;
index 6f891d8..4ca0786 100644 (file)
@@ -2217,8 +2217,7 @@ static ssize_t radeon_show_edid1(struct file *filp, struct kobject *kobj,
                                 char *buf, loff_t off, size_t count)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
-       struct pci_dev *pdev = to_pci_dev(dev);
-        struct fb_info *info = pci_get_drvdata(pdev);
+       struct fb_info *info = dev_get_drvdata(dev);
         struct radeonfb_info *rinfo = info->par;
 
        return radeon_show_one_edid(buf, off, count, rinfo->mon1_EDID);
@@ -2230,8 +2229,7 @@ static ssize_t radeon_show_edid2(struct file *filp, struct kobject *kobj,
                                 char *buf, loff_t off, size_t count)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
-       struct pci_dev *pdev = to_pci_dev(dev);
-        struct fb_info *info = pci_get_drvdata(pdev);
+       struct fb_info *info = dev_get_drvdata(dev);
         struct radeonfb_info *rinfo = info->par;
 
        return radeon_show_one_edid(buf, off, count, rinfo->mon2_EDID);
index 04a2266..51d97ec 100644 (file)
@@ -122,28 +122,13 @@ static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si)
  */
 static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
 {
-       static const int default_resolutions[][2] = {
-               {  800,  600 },
-               { 1024,  768 },
-               { 1280, 1024 },
-       };
-       u32 i, right_margin;
-
-       for (i = 0; i < ARRAY_SIZE(default_resolutions); i++) {
-               if (default_resolutions[i][0] == si->lfb_width &&
-                   default_resolutions[i][1] == si->lfb_height)
-                       break;
-       }
-       /* If not a default resolution used for textmode, this should be fine */
-       if (i >= ARRAY_SIZE(default_resolutions))
-               return true;
-
-       /* If the right margin is 5 times smaller then the left one, reject */
-       right_margin = si->lfb_width - (bgrt_tab.image_offset_x + bmp_width);
-       if (right_margin < (bgrt_tab.image_offset_x / 5))
-               return false;
+       /*
+        * All x86 firmwares horizontally center the image (the yoffset
+        * calculations differ between boards, but xoffset is predictable).
+        */
+       u32 expected_xoffset = (si->lfb_width - bmp_width) / 2;
 
-       return true;
+       return bgrt_tab.image_offset_x == expected_xoffset;
 }
 #else
 static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
index 0ffc1b7..1541272 100644 (file)
@@ -153,13 +153,11 @@ EXPORT_SYMBOL_GPL(mmp_get_path);
 struct mmp_path *mmp_register_path(struct mmp_path_info *info)
 {
        int i;
-       size_t size;
        struct mmp_path *path = NULL;
        struct mmp_panel *panel;
 
-       size = sizeof(struct mmp_path)
-               + sizeof(struct mmp_overlay) * info->overlay_num;
-       path = kzalloc(size, GFP_KERNEL);
+       path = kzalloc(struct_size(path, overlays, info->overlay_num),
+                      GFP_KERNEL);
        if (!path)
                return NULL;
 
index 7ff4b6b..0a3b2b7 100644 (file)
@@ -458,13 +458,11 @@ static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
        set_color_bitfields(var);
 
        if (var->vmode & FB_VMODE_YWRAP) {
-               if (var->xoffset || var->yoffset < 0 ||
-                   var->yoffset >= var->yres_virtual) {
+               if (var->xoffset || var->yoffset >= var->yres_virtual) {
                        var->xoffset = var->yoffset = 0;
                } else {
                        if (var->xoffset > var->xres_virtual - var->xres ||
-                           var->yoffset > var->yres_virtual - var->yres ||
-                           var->xoffset < 0 || var->yoffset < 0)
+                           var->yoffset > var->yres_virtual - var->yres)
                                var->xoffset = var->yoffset = 0;
                }
        } else {
index ac0bcac..c249763 100644 (file)
@@ -1594,6 +1594,7 @@ sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV21:
                info->fix.ypanstep = 2;
+               /* Fall through */
        case V4L2_PIX_FMT_NV16:
        case V4L2_PIX_FMT_NV61:
                info->fix.xpanstep = 2;
@@ -2084,6 +2085,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV21:
                info->fix.ypanstep = 2;
+               /* Fall through */
        case V4L2_PIX_FMT_NV16:
        case V4L2_PIX_FMT_NV61:
                info->fix.xpanstep = 2;
index 7b1b0d8..207d0ad 100644 (file)
@@ -1694,10 +1694,8 @@ static void smtcfb_pci_remove(struct pci_dev *pdev)
 
 static int __maybe_unused smtcfb_pci_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct smtcfb_info *sfb;
+       struct smtcfb_info *sfb = dev_get_drvdata(device);
 
-       sfb = pci_get_drvdata(pdev);
 
        /* set the hw in sleep mode use external clock and self memory refresh
         * so that we can turn off internal PLLs later on
@@ -1717,10 +1715,8 @@ static int __maybe_unused smtcfb_pci_suspend(struct device *device)
 
 static int __maybe_unused smtcfb_pci_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct smtcfb_info *sfb;
+       struct smtcfb_info *sfb = dev_get_drvdata(device);
 
-       sfb = pci_get_drvdata(pdev);
 
        /* reinit hardware */
        sm7xx_init_hw();
index c328e82..fe373b6 100644 (file)
@@ -1183,7 +1183,7 @@ static int dlfb_ops_blank(int blank_mode, struct fb_info *info)
        return 0;
 }
 
-static struct fb_ops dlfb_ops = {
+static const struct fb_ops dlfb_ops = {
        .owner = THIS_MODULE,
        .fb_read = fb_sys_read,
        .fb_write = dlfb_ops_write,
index e2b2062..ffa2ca2 100644 (file)
@@ -221,49 +221,6 @@ void viafb_release_dma(void)
 }
 EXPORT_SYMBOL_GPL(viafb_release_dma);
 
-
-#if 0
-/*
- * Copy a single buffer from FB memory, synchronously.  This code works
- * but is not currently used.
- */
-void viafb_dma_copy_out(unsigned int offset, dma_addr_t paddr, int len)
-{
-       unsigned long flags;
-       int csr;
-
-       mutex_lock(&viafb_dma_lock);
-       init_completion(&viafb_dma_completion);
-       /*
-        * Program the controller.
-        */
-       spin_lock_irqsave(&global_dev.reg_lock, flags);
-       viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_DONE);
-       /* Enable ints; must happen after CSR0 write! */
-       viafb_mmio_write(VDMA_MR0, VDMA_MR_TDIE);
-       viafb_mmio_write(VDMA_MARL0, (int) (paddr & 0xfffffff0));
-       viafb_mmio_write(VDMA_MARH0, (int) ((paddr >> 28) & 0xfff));
-       /* Data sheet suggests DAR0 should be <<4, but it lies */
-       viafb_mmio_write(VDMA_DAR0, offset);
-       viafb_mmio_write(VDMA_DQWCR0, len >> 4);
-       viafb_mmio_write(VDMA_TMR0, 0);
-       viafb_mmio_write(VDMA_DPRL0, 0);
-       viafb_mmio_write(VDMA_DPRH0, 0);
-       viafb_mmio_write(VDMA_PMR0, 0);
-       csr = viafb_mmio_read(VDMA_CSR0);
-       viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_START);
-       spin_unlock_irqrestore(&global_dev.reg_lock, flags);
-       /*
-        * Now we just wait until the interrupt handler says
-        * we're done.
-        */
-       wait_for_completion_interruptible(&viafb_dma_completion);
-       viafb_mmio_write(VDMA_MR0, 0); /* Reset int enable */
-       mutex_unlock(&viafb_dma_lock);
-}
-EXPORT_SYMBOL_GPL(viafb_dma_copy_out);
-#endif
-
 /*
  * Do a scatter/gather DMA copy from FB memory.  You must have done
  * a successful call to viafb_request_dma() first.