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 -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCXV+lGwAKCRDj7w1vZxhR xf9gAP9Fz/6pEYVP/IMHk3UH5wCVTGTNGE5Ji21PSS+FbfbprgEA7GFCkvgSCk2I URWLVq2VLgDimHNB3IOEhOE7PDzrxg0= =LMbV -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2019-08-23' of git://anongit.freedesktop.org/drm/drm-misc into drm-next 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
This commit is contained in:
commit
c87237110f
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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__ */
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -30,19 +30,14 @@
|
|||
#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 drm_crtc *crtc;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
|
|
@ -29,46 +29,146 @@
|
|||
|
||||
#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 void kirin_drm_mode_config_init(struct drm_device *dev)
|
||||
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)
|
||||
{
|
||||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
int ret = 0;
|
||||
|
||||
dev->mode_config.max_width = 2048;
|
||||
dev->mode_config.max_height = 2048;
|
||||
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;
|
||||
}
|
||||
|
||||
dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
|
||||
drm_plane_helper_add(plane, data->plane_helper_funcs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kirin_drm_kms_init(struct drm_device *dev)
|
||||
static void kirin_drm_private_cleanup(struct drm_device *dev)
|
||||
{
|
||||
struct kirin_drm_private *kirin_priv = dev->dev_private;
|
||||
struct kirin_drm_data *data;
|
||||
|
||||
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);
|
||||
|
||||
devm_kfree(dev->dev, kirin_priv);
|
||||
dev->dev_private = NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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_set_drvdata(dev->dev, dev);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return drm_gem_cma_dumb_create_internal(file, dev, args);
|
||||
}
|
||||
|
||||
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 compare_of(struct device *dev, void *data)
|
||||
{
|
||||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kirin_drm_connectors_register(struct drm_device *dev)
|
||||
{
|
||||
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 */ },
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
||||
/* display controller init/cleanup ops */
|
||||
struct kirin_dc_ops {
|
||||
int (*init)(struct platform_device *pdev);
|
||||
void (*cleanup)(struct platform_device *pdev);
|
||||
#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;
|
||||
};
|
||||
|
||||
extern const struct kirin_dc_ops ade_dc_ops;
|
||||
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_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 struct kirin_drm_data ade_driver_data;
|
||||
|
||||
#endif /* __KIRIN_DRM_DRV_H__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) |
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
nvbo = *pnvbo;
|
||||
}
|
||||
|
||||
ret = nouveau_bo_init(nvbo, size, align, flags, NULL, NULL);
|
||||
if (ret) {
|
||||
nouveau_bo_ref(NULL, &nvbo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
@ -22,12 +23,6 @@
|
|||
#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;
|
||||
|
||||
drm_mm_for_each_node(node, &pfdev->mm) {
|
||||
if (offset >= node->start && offset < (node->start + node->size))
|
||||
return node;
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
@ -2395,17 +2377,6 @@ static int aty_init(struct fb_info *info)
|
|||
case CLK_IBMRGB514:
|
||||
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!");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
return bgrt_tab.image_offset_x == expected_xoffset;
|
||||
}
|
||||
#else
|
||||
static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue