Commit 14d2bd53 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-misc-next-2019-04-04' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.2:

UAPI Changes:
-syncobj: Add TIMELINE_WAIT|QUERY|TRANSFER|TIMELINE_SIGNAL ioctls (Chunming)
-Clarify that 1.0 can be represented by drm_color_lut (Daniel)

Cross-subsystem Changes:
-dt-bindings: Add binding for rk3066 hdmi (Johan)
-dt-bindings: Add binding for Feiyang FY07024DI26A30-D panel (Jagan)
-dt-bindings: Add Rocktech vendor prefix and jh057n00900 panel bindings (Guido)
-MAINTAINERS: Add lima and ASPEED entries (Joel & Qiang)

Core Changes:
-memory: use dma_alloc_coherent when mem encryption is active (Christian)
-dma_buf: add support for a dma_fence chain (Christian)
-shmem_gem: fix off-by-one bug in new shmem gem helpers (Dan)

Driver Changes:
-rockchip: Add support for rk3066 hdmi (Johan)
-ASPEED: Add driver supporting ASPEED BMC display controller to drm (Joel)
-lima: Add driver supporting Arm Mali4xx gpus to drm (Qiang)
-vc4/v3d: Various cleanups and improved error handling (Eric)
-panel: Add support for Feiyang FY07024DI26A30-D MIPI-DSI panel (Jagan)
-panel: Add support for Rocktech jh057n00900 MIPI-DSI panel (Guido)

Cc: Johan Jonker <jbx6244@gmail.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Chunming Zhou <david1.zhou@amd.com>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Eric Anholt <eric@anholt.net>
Cc: Qiang Yu <yuq825@gmail.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Cc: Guido Günther <agx@sigxcpu.org>
Cc: Joel Stanley <joel@jms.id.au>
[airlied: fixed XA limit build breakage, Rodrigo also submitted the same patch, but
I squashed it in the merge.]
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20190404201016.GA139524@art_vandelay
parents 5ebffda2 f15a3ea8
Feiyang FY07024DI26A30-D 7" MIPI-DSI LCD Panel
Required properties:
- compatible: must be "feiyang,fy07024di26a30d"
- reg: DSI virtual channel used by that screen
- avdd-supply: analog regulator dc1 switch
- dvdd-supply: 3v3 digital regulator
- reset-gpios: a GPIO phandle for the reset pin
Optional properties:
- backlight: phandle for the backlight control.
panel@0 {
compatible = "feiyang,fy07024di26a30d";
reg = <0>;
avdd-supply = <&reg_dc1sw>;
dvdd-supply = <&reg_dldo2>;
reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* LCD-RST: PD24 */
backlight = <&backlight>;
};
......@@ -12,7 +12,7 @@ Optional properties:
Example:
&mipi_dsi {
panel {
panel@0 {
compatible = "innolux,p079zca";
reg = <0>;
power-supply = <...>;
......
......@@ -13,7 +13,7 @@ Optional properties:
Example:
&mipi_dsi {
panel {
panel@0 {
compatible = "innolux,p079zca";
reg = <0>;
avdd-supply = <...>;
......
......@@ -12,7 +12,7 @@ Optional properties:
Example:
&mipi_dsi {
panel {
panel@0 {
compatible = "kingdisplay,kd097d04";
reg = <0>;
power-supply = <...>;
......
Rocktech jh057n00900 5.5" 720x1440 TFT LCD panel
Required properties:
- compatible: should be "rocktech,jh057n00900"
- reg: DSI virtual channel of the peripheral
- reset-gpios: panel reset gpio
- backlight: phandle of the backlight device attached to the panel
Example:
&mipi_dsi {
panel@0 {
compatible = "rocktech,jh057n00900";
reg = <0>;
backlight = <&backlight>;
reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
};
};
Rockchip specific extensions for rk3066 HDMI
============================================
Required properties:
- compatible:
"rockchip,rk3066-hdmi";
- reg:
Physical base address and length of the controller's registers.
- clocks, clock-names:
Phandle to HDMI controller clock, name should be "hclk".
- interrupts:
HDMI interrupt number.
- power-domains:
Phandle to the RK3066_PD_VIO power domain.
- rockchip,grf:
This soc uses GRF regs to switch the HDMI TX input between vop0 and vop1.
- ports:
Contains one port node with two endpoints, numbered 0 and 1,
connected respectively to vop0 and vop1.
Contains one port node with one endpoint
connected to a hdmi-connector node.
- pinctrl-0, pinctrl-name:
Switch the iomux for the HPD/I2C pins to HDMI function.
Example:
hdmi: hdmi@10116000 {
compatible = "rockchip,rk3066-hdmi";
reg = <0x10116000 0x2000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru HCLK_HDMI>;
clock-names = "hclk";
power-domains = <&power RK3066_PD_VIO>;
rockchip,grf = <&grf>;
pinctrl-names = "default";
pinctrl-0 = <&hdmii2c_xfer>, <&hdmi_hpd>;
ports {
#address-cells = <1>;
#size-cells = <0>;
hdmi_in: port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
hdmi_in_vop0: endpoint@0 {
reg = <0>;
remote-endpoint = <&vop0_out_hdmi>;
};
hdmi_in_vop1: endpoint@1 {
reg = <1>;
remote-endpoint = <&vop1_out_hdmi>;
};
};
hdmi_out: port@1 {
reg = <1>;
hdmi_out_con: endpoint {
remote-endpoint = <&hdmi_con_in>;
};
};
};
};
&pinctrl {
hdmi {
hdmi_hpd: hdmi-hpd {
rockchip,pins = <0 RK_PA0 1 &pcfg_pull_default>;
};
hdmii2c_xfer: hdmii2c-xfer {
rockchip,pins = <0 RK_PA1 1 &pcfg_pull_none>,
<0 RK_PA2 1 &pcfg_pull_none>;
};
};
};
Device tree configuration for the GFX display device on the ASPEED SoCs
Required properties:
- compatible
* Must be one of the following:
+ aspeed,ast2500-gfx
+ aspeed,ast2400-gfx
* In addition, the ASPEED pinctrl bindings require the 'syscon' property to
be present
- reg: Physical base address and length of the GFX registers
- interrupts: interrupt number for the GFX device
- clocks: clock number used to generate the pixel clock
- resets: reset line that must be released to use the GFX device
- memory-region:
Phandle to a memory region to allocate from, as defined in
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
Example:
gfx: display@1e6e6000 {
compatible = "aspeed,ast2500-gfx", "syscon";
reg = <0x1e6e6000 0x1000>;
reg-io-width = <4>;
clocks = <&syscon ASPEED_CLK_GATE_D1CLK>;
resets = <&syscon ASPEED_RESET_CRT1>;
interrupts = <0x19>;
memory-region = <&gfx_memory>;
};
gfx_memory: framebuffer {
size = <0x01000000>;
alignment = <0x01000000>;
compatible = "shared-dma-pool";
reusable;
};
......@@ -345,6 +345,7 @@ ricoh Ricoh Co. Ltd.
rikomagic Rikomagic Tech Corp. Ltd
riscv RISC-V Foundation
rockchip Fuzhou Rockchip Electronics Co., Ltd
rocktech ROCKTECH DISPLAYS LIMITED
rohm ROHM Semiconductor Co., Ltd
ronbo Ronbo Electronics
roofull Shenzhen Roofull Technology Co, Ltd
......
......@@ -4894,6 +4894,14 @@ M: Dave Airlie <airlied@redhat.com>
S: Odd Fixes
F: drivers/gpu/drm/ast/
DRM DRIVER FOR ASPEED BMC GFX
M: Joel Stanley <joel@jms.id.au>
L: linux-aspeed@lists.ozlabs.org
T: git git://anongit.freedesktop.org/drm/drm-misc
S: Supported
F: drivers/gpu/drm/aspeed/
F: Documentation/devicetree/bindings/gpu/aspeed-gfx.txt
DRM DRIVER FOR BOCHS VIRTUAL GPU
M: Gerd Hoffmann <kraxel@redhat.com>
L: virtualization@lists.linux-foundation.org
......@@ -4907,6 +4915,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/tve200/
DRM DRIVER FOR FEIYANG FY07024DI26A30-D MIPI-DSI LCD PANELS
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
F: drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
F: Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt
DRM DRIVER FOR ILITEK ILI9225 PANELS
M: David Lechner <david@lechnology.com>
S: Maintained
......@@ -4998,6 +5012,12 @@ S: Orphan / Obsolete
F: drivers/gpu/drm/r128/
F: include/uapi/drm/r128_drm.h
DRM DRIVER FOR ROCKTECH JH057N00900 PANELS
M: Guido Günther <agx@sigxcpu.org>
S: Maintained
F: drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c
F: Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.txt
DRM DRIVER FOR SAVAGE VIDEO CARDS
S: Orphan / Obsolete
F: drivers/gpu/drm/savage/
......@@ -5186,6 +5206,15 @@ S: Maintained
F: drivers/gpu/drm/hisilicon/
F: Documentation/devicetree/bindings/display/hisilicon/
DRM DRIVERS FOR LIMA
M: Qiang Yu <yuq825@gmail.com>
L: dri-devel@lists.freedesktop.org
L: lima@lists.freedesktop.org
S: Maintained
F: drivers/gpu/drm/lima/
F: include/uapi/drm/lima_drm.h
T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVERS FOR MEDIATEK
M: CK Hu <ck.hu@mediatek.com>
M: Philipp Zabel <p.zabel@pengutronix.de>
......
obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o
obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
reservation.o seqno-fence.o
obj-$(CONFIG_SYNC_FILE) += sync_file.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
obj-$(CONFIG_UDMABUF) += udmabuf.o
/*
* fence-chain: chain fences together in a timeline
*
* Copyright (C) 2018 Advanced Micro Devices, Inc.
* Authors:
* Christian König <christian.koenig@amd.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/dma-fence-chain.h>
static bool dma_fence_chain_enable_signaling(struct dma_fence *fence);
/**
* dma_fence_chain_get_prev - use RCU to get a reference to the previous fence
* @chain: chain node to get the previous node from
*
* Use dma_fence_get_rcu_safe to get a reference to the previous fence of the
* chain node.
*/
static struct dma_fence *dma_fence_chain_get_prev(struct dma_fence_chain *chain)
{
struct dma_fence *prev;
rcu_read_lock();
prev = dma_fence_get_rcu_safe(&chain->prev);
rcu_read_unlock();
return prev;
}
/**
* dma_fence_chain_walk - chain walking function
* @fence: current chain node
*
* Walk the chain to the next node. Returns the next fence or NULL if we are at
* the end of the chain. Garbage collects chain nodes which are already
* signaled.
*/
struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
{
struct dma_fence_chain *chain, *prev_chain;
struct dma_fence *prev, *replacement, *tmp;
chain = to_dma_fence_chain(fence);
if (!chain) {
dma_fence_put(fence);
return NULL;
}
while ((prev = dma_fence_chain_get_prev(chain))) {
prev_chain = to_dma_fence_chain(prev);
if (prev_chain) {
if (!dma_fence_is_signaled(prev_chain->fence))
break;
replacement = dma_fence_chain_get_prev(prev_chain);
} else {
if (!dma_fence_is_signaled(prev))
break;
replacement = NULL;
}
tmp = cmpxchg((void **)&chain->prev, (void *)prev, (void *)replacement);
if (tmp == prev)
dma_fence_put(tmp);
else
dma_fence_put(replacement);
dma_fence_put(prev);
}
dma_fence_put(fence);
return prev;
}
EXPORT_SYMBOL(dma_fence_chain_walk);
/**
* dma_fence_chain_find_seqno - find fence chain node by seqno
* @pfence: pointer to the chain node where to start
* @seqno: the sequence number to search for
*
* Advance the fence pointer to the chain node which will signal this sequence
* number. If no sequence number is provided then this is a no-op.
*
* Returns EINVAL if the fence is not a chain node or the sequence number has
* not yet advanced far enough.
*/
int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno)
{
struct dma_fence_chain *chain;
if (!seqno)
return 0;
chain = to_dma_fence_chain(*pfence);
if (!chain || chain->base.seqno < seqno)
return -EINVAL;
dma_fence_chain_for_each(*pfence, &chain->base) {
if ((*pfence)->context != chain->base.context ||
to_dma_fence_chain(*pfence)->prev_seqno < seqno)
break;
}
dma_fence_put(&chain->base);
return 0;
}
EXPORT_SYMBOL(dma_fence_chain_find_seqno);
static const char *dma_fence_chain_get_driver_name(struct dma_fence *fence)
{
return "dma_fence_chain";
}
static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence)
{
return "unbound";
}
static void dma_fence_chain_irq_work(struct irq_work *work)
{
struct dma_fence_chain *chain;
chain = container_of(work, typeof(*chain), work);
/* Try to rearm the callback */
if (!dma_fence_chain_enable_signaling(&chain->base))
/* Ok, we are done. No more unsignaled fences left */
dma_fence_signal(&chain->base);
dma_fence_put(&chain->base);
}
static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
{
struct dma_fence_chain *chain;
chain = container_of(cb, typeof(*chain), cb);
irq_work_queue(&chain->work);
dma_fence_put(f);
}
static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
{
struct dma_fence_chain *head = to_dma_fence_chain(fence);
dma_fence_get(&head->base);
dma_fence_chain_for_each(fence, &head->base) {
struct dma_fence_chain *chain = to_dma_fence_chain(fence);
struct dma_fence *f = chain ? chain->fence : fence;
dma_fence_get(f);
if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) {
dma_fence_put(fence);
return true;
}
dma_fence_put(f);
}
dma_fence_put(&head->base);
return false;
}
static bool dma_fence_chain_signaled(struct dma_fence *fence)
{
dma_fence_chain_for_each(fence, fence) {
struct dma_fence_chain *chain = to_dma_fence_chain(fence);
struct dma_fence *f = chain ? chain->fence : fence;
if (!dma_fence_is_signaled(f)) {
dma_fence_put(fence);
return false;
}
}
return true;
}
static void dma_fence_chain_release(struct dma_fence *fence)
{
struct dma_fence_chain *chain = to_dma_fence_chain(fence);
dma_fence_put(rcu_dereference_protected(chain->prev, true));
dma_fence_put(chain->fence);
dma_fence_free(fence);
}
const struct dma_fence_ops dma_fence_chain_ops = {
.get_driver_name = dma_fence_chain_get_driver_name,
.get_timeline_name = dma_fence_chain_get_timeline_name,
.enable_signaling = dma_fence_chain_enable_signaling,
.signaled = dma_fence_chain_signaled,
.release = dma_fence_chain_release,
};
EXPORT_SYMBOL(dma_fence_chain_ops);
/**
* dma_fence_chain_init - initialize a fence chain
* @chain: the chain node to initialize
* @prev: the previous fence
* @fence: the current fence
*
* Initialize a new chain node and either start a new chain or add the node to
* the existing chain of the previous fence.
*/
void dma_fence_chain_init(struct dma_fence_chain *chain,
struct dma_fence *prev,
struct dma_fence *fence,
uint64_t seqno)
{
struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
uint64_t context;
spin_lock_init(&chain->lock);
rcu_assign_pointer(chain->prev, prev);
chain->fence = fence;
chain->prev_seqno = 0;
init_irq_work(&chain->work, dma_fence_chain_irq_work);
/* Try to reuse the context of the previous chain node. */
if (prev_chain && __dma_fence_is_later(seqno, prev->seqno)) {
context = prev->context;
chain->prev_seqno = prev->seqno;
} else {
context = dma_fence_context_alloc(1);
/* Make sure that we always have a valid sequence number. */
if (prev_chain)
seqno = max(prev->seqno, seqno);
}
dma_fence_init(&chain->base, &dma_fence_chain_ops,
&chain->lock, context, seqno);
}
EXPORT_SYMBOL(dma_fence_chain_init);
......@@ -335,6 +335,10 @@ source "drivers/gpu/drm/xen/Kconfig"
source "drivers/gpu/drm/vboxvideo/Kconfig"
source "drivers/gpu/drm/lima/Kconfig"
source "drivers/gpu/drm/aspeed/Kconfig"
# Keep legacy drivers last
menuconfig DRM_LEGACY
......
......@@ -110,3 +110,5 @@ obj-$(CONFIG_DRM_PL111) += pl111/
obj-$(CONFIG_DRM_TVE200) += tve200/
obj-$(CONFIG_DRM_XEN) += xen/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_DRM_LIMA) += lima/
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
config DRM_ASPEED_GFX
tristate "ASPEED BMC Display Controller"
depends on DRM && OF
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_PANEL
select DMA_CMA
select CMA
select MFD_SYSCON
help
Chose this option if you have an ASPEED AST2500 SOC Display
Controller (aka GFX).
If M is selected this module will be called aspeed_gfx.
aspeed_gfx-y := aspeed_gfx_drv.o aspeed_gfx_crtc.o aspeed_gfx_out.o
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed_gfx.o
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright 2018 IBM Corporation */
#include <drm/drm_device.h>
#include <drm/drm_simple_kms_helper.h>
struct aspeed_gfx {
void __iomem *base;
struct clk *clk;
struct reset_control *rst;
struct regmap *scu;
struct drm_simple_display_pipe pipe;
struct drm_connector connector;
struct drm_fbdev_cma *fbdev;
};
int aspeed_gfx_create_pipe(struct drm_device *drm);
int aspeed_gfx_create_output(struct drm_device *drm);
#define CRT_CTRL1 0x60 /* CRT Control I */
#define CRT_CTRL2 0x64 /* CRT Control II */
#define CRT_STATUS 0x68 /* CRT Status */
#define CRT_MISC 0x6c /* CRT Misc Setting */
#define CRT_HORIZ0 0x70 /* CRT Horizontal Total & Display Enable End */
#define CRT_HORIZ1 0x74 /* CRT Horizontal Retrace Start & End */
#define CRT_VERT0 0x78 /* CRT Vertical Total & Display Enable End */
#define CRT_VERT1 0x7C /* CRT Vertical Retrace Start & End */
#define CRT_ADDR 0x80 /* CRT Display Starting Address */
#define CRT_OFFSET 0x84 /* CRT Display Offset & Terminal Count */
#define CRT_THROD 0x88 /* CRT Threshold */
#define CRT_XSCALE 0x8C /* CRT Scaling-Up Factor */
#define CRT_CURSOR0 0x90 /* CRT Hardware Cursor X & Y Offset */
#define CRT_CURSOR1 0x94 /* CRT Hardware Cursor X & Y Position */
#define CRT_CURSOR2 0x98 /* CRT Hardware Cursor Pattern Address */
#define CRT_9C 0x9C
#define CRT_OSD_H 0xA0 /* CRT OSD Horizontal Start/End */
#define CRT_OSD_V 0xA4 /* CRT OSD Vertical Start/End */
#define CRT_OSD_ADDR 0xA8 /* CRT OSD Pattern Address */
#define CRT_OSD_DISP 0xAC /* CRT OSD Offset */
#define CRT_OSD_THRESH 0xB0 /* CRT OSD Threshold & Alpha */
#define CRT_B4 0xB4
#define CRT_STS_V 0xB8 /* CRT Status V */
#define CRT_SCRATCH 0xBC /* Scratchpad */
#define CRT_BB0_ADDR 0xD0 /* CRT Display BB0 Starting Address */
#define CRT_BB1_ADDR 0xD4 /* CRT Display BB1 Starting Address */
#define CRT_BB_COUNT 0xD8 /* CRT Display BB Terminal Count */
#define OSD_COLOR1 0xE0 /* OSD Color Palette Index 1 & 0 */
#define OSD_COLOR2 0xE4 /* OSD Color Palette Index 3 & 2 */
#define OSD_COLOR3 0xE8 /* OSD Color Palette Index 5 & 4 */
#define OSD_COLOR4 0xEC /* OSD Color Palette Index 7 & 6 */
#define OSD_COLOR5 0xF0 /* OSD Color Palette Index 9 & 8 */
#define OSD_COLOR6 0xF4 /* OSD Color Palette Index 11 & 10 */
#define OSD_COLOR7 0xF8 /* OSD Color Palette Index 13 & 12 */
#define OSD_COLOR8 0xFC /* OSD Color Palette Index 15 & 14 */
/* CTRL1 */
#define CRT_CTRL_EN BIT(0)
#define CRT_CTRL_HW_CURSOR_EN BIT(1)
#define CRT_CTRL_OSD_EN BIT(2)
#define CRT_CTRL_INTERLACED BIT(3)
#define CRT_CTRL_COLOR_RGB565 (0 << 7)
#define CRT_CTRL_COLOR_YUV444 (1 << 7)
#define CRT_CTRL_COLOR_XRGB8888 (2 << 7)
#define CRT_CTRL_COLOR_RGB888 (3 << 7)
#define CRT_CTRL_COLOR_YUV444_2RGB (5 << 7)
#define CRT_CTRL_COLOR_YUV422 (7 << 7)
#define CRT_CTRL_COLOR_MASK GENMASK(9, 7)
#define CRT_CTRL_HSYNC_NEGATIVE BIT(16)
#define CRT_CTRL_VSYNC_NEGATIVE BIT(17)
#define CRT_CTRL_VERTICAL_INTR_EN BIT(30)
#define CRT_CTRL_VERTICAL_INTR_STS BIT(31)
/* CTRL2 */
#define CRT_CTRL_DAC_EN BIT(0)
#define CRT_CTRL_VBLANK_LINE(x) (((x) << 20) & CRT_CTRL_VBLANK_LINE_MASK)
#define CRT_CTRL_VBLANK_LINE_MASK GENMASK(20, 31)
/* CRT_HORIZ0 */
#define CRT_H_TOTAL(x) (x)
#define CRT_H_DE(x) ((x) << 16)
/* CRT_HORIZ1 */
#define CRT_H_RS_START(x) (x)
#define CRT_H_RS_END(x) ((x) << 16)
/* CRT_VIRT0 */
#define CRT_V_TOTAL(x) (x)
#define CRT_V_DE(x) ((x) << 16)
/* CRT_VIRT1 */
#define CRT_V_RS_START(x) (x)
#define CRT_V_RS_END(x) ((x) << 16)
/* CRT_OFFSET */
#define CRT_DISP_OFFSET(x) (x)
#define CRT_TERM_COUNT(x) ((x) << 16)
/* CRT_THROD */
#define CRT_THROD_LOW(x) (x)
#define CRT_THROD_HIGH(x) ((x) << 8)
/* Default Threshold Seting */
#define G5_CRT_THROD_VAL (CRT_THROD_LOW(0x24) | CRT_THROD_HIGH(0x3C))
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2018 IBM Corporation
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/regmap.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_panel.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include "aspeed_gfx.h"
static struct aspeed_gfx *
drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe)
{
return container_of(pipe, struct aspeed_gfx, pipe);
}
static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
{
struct drm_crtc *crtc = &priv->pipe.crtc;
struct drm_device *drm = crtc->dev;
const u32 format = crtc->primary->state->fb->format->format;
u32 ctrl1;
ctrl1 = readl(priv->base + CRT_CTRL1);
ctrl1 &= ~CRT_CTRL_COLOR_MASK;
switch (format) {
case DRM_FORMAT_RGB565:
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
ctrl1 |= CRT_CTRL_COLOR_RGB565;
*bpp = 16;
break;
case DRM_FORMAT_XRGB8888:
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
ctrl1 |= CRT_CTRL_COLOR_XRGB8888;
*bpp = 32;
break;
default:
dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
return -EINVAL;
}
writel(ctrl1, priv->base + CRT_CTRL1);
return 0;
}
static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv)
{
u32 ctrl1 = readl(priv->base + CRT_CTRL1);
u32 ctrl2 = readl(priv->base + CRT_CTRL2);
/* SCU2C: set DAC source for display output to Graphics CRT (GFX) */
regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16));
writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1);
writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
}
static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
{
u32 ctrl1 = readl(priv->base + CRT_CTRL1);
u32 ctrl2 = readl(priv->base + CRT_CTRL2);
writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1);
writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
regmap_update_bits(priv->scu, 0x2c, BIT(16), 0);
}
static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
{
struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
u32 ctrl1, d_offset, t_count, bpp;
int err;
err = aspeed_gfx_set_pixel_fmt(priv, &bpp);
if (err)
return;
#if 0
/* TODO: we have only been able to test with the 40MHz USB clock. The
* clock is fixed, so we cannot adjust it here. */
clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000);
#endif
ctrl1 = readl(priv->base + CRT_CTRL1);
ctrl1 &= ~(CRT_CTRL_INTERLACED |
CRT_CTRL_HSYNC_NEGATIVE |
CRT_CTRL_VSYNC_NEGATIVE);
if (m->flags & DRM_MODE_FLAG_INTERLACE)
ctrl1 |= CRT_CTRL_INTERLACED;
if (!(m->flags & DRM_MODE_FLAG_PHSYNC))
ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE;
if (!(m->flags & DRM_MODE_FLAG_PVSYNC))
ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE;
writel(ctrl1, priv->base + CRT_CTRL1);
/* Horizontal timing */
writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1),
priv->base + CRT_HORIZ0);
writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end),
priv->base + CRT_HORIZ1);
/* Vertical timing */
writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1),
priv->base + CRT_VERT0);
writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end),
priv->base + CRT_VERT1);
/*
* Display Offset: address difference between consecutive scan lines
* Terminal Count: memory size of one scan line
*/
d_offset = m->hdisplay * bpp / 8;
t_count = (m->hdisplay * bpp + 127) / 128;
writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count),
priv->base + CRT_OFFSET);
/*
* Threshold: FIFO thresholds of refill and stop (16 byte chunks
* per line, rounded up)
*/
writel(G5_CRT_THROD_VAL, priv->base + CRT_THROD);
}
static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
struct drm_crtc *crtc = &pipe->crtc;
aspeed_gfx_crtc_mode_set_nofb(priv);
aspeed_gfx_enable_controller(priv);
drm_crtc_vblank_on(crtc);
}
static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe)
{
struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
struct drm_crtc *crtc = &pipe->crtc;
drm_crtc_vblank_off(crtc);
aspeed_gfx_disable_controller(priv);
}
static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
{
struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
struct drm_crtc *crtc = &pipe->crtc;
struct drm_framebuffer *fb = pipe->plane.state->fb;
struct drm_pending_vblank_event *event;
struct drm_gem_cma_object *gem;
spin_lock_irq(&crtc->dev->event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
}
spin_unlock_irq(&crtc->dev->event_lock);
if (!fb)
return;
gem = drm_fb_cma_get_gem_obj(fb, 0);
if (!gem)
return;
writel(gem->paddr, priv->base + CRT_ADDR);
}
static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
{
struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
u32 reg = readl(priv->base + CRT_CTRL1);
/* Clear pending VBLANK IRQ */
writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
reg |= CRT_CTRL_VERTICAL_INTR_EN;
writel(reg, priv->base + CRT_CTRL1);
return 0;
}
static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
{
struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
u32 reg = readl(priv->base + CRT_CTRL1);
reg &= ~CRT_CTRL_VERTICAL_INTR_EN;
writel(reg, priv->base + CRT_CTRL1);
/* Clear pending VBLANK IRQ */
writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
}
static struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
.enable = aspeed_gfx_pipe_enable,
.disable = aspeed_gfx_pipe_disable,
.update = aspeed_gfx_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
.enable_vblank = aspeed_gfx_enable_vblank,
.disable_vblank = aspeed_gfx_disable_vblank,
};
static const uint32_t aspeed_gfx_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
};
int aspeed_gfx_create_pipe(struct drm_device *drm)
{
struct aspeed_gfx *priv = drm->dev_private;
return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs,
aspeed_gfx_formats,
ARRAY_SIZE(aspeed_gfx_formats),
NULL,
&priv->connector);
}
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2018 IBM Corporation
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_drv.h>
#include "aspeed_gfx.h"
/**
* DOC: ASPEED GFX Driver
*
* This driver is for the ASPEED BMC SoC's 'GFX' display hardware, also called
* the 'SOC Display Controller' in the datasheet. This driver runs on the ARM
* based BMC systems, unlike the ast driver which runs on a host CPU and is for
* a PCIe graphics device.
*
* The AST2500 supports a total of 3 output paths:
*
* 1. VGA output, the output target can choose either or both to the DAC
* or DVO interface.
*
* 2. Graphics CRT output, the output target can choose either or both to
* the DAC or DVO interface.
*
* 3. Video input from DVO, the video input can be used for video engine
* capture or DAC display output.
*
* Output options are selected in SCU2C.
*
* The "VGA mode" device is the PCI attached controller. The "Graphics CRT"
* is the ARM's internal display controller.
*
* The driver only supports a simple configuration consisting of a 40MHz
* pixel clock, fixed by hardware limitations, and the VGA output path.
*
* The driver was written with the 'AST2500 Software Programming Guide' v17,
* which is available under NDA from ASPEED.
*/
static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = {
.fb_create = drm_gem_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static void aspeed_gfx_setup_mode_config(struct drm_device *drm)
{
drm_mode_config_init(drm);
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 800;
drm->mode_config.max_height = 600;
drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs;
}
static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
{
struct drm_device *drm = data;
struct aspeed_gfx *priv = drm->dev_private;
u32 reg;
reg = readl(priv->base + CRT_CTRL1);
if (reg & CRT_CTRL_VERTICAL_INTR_STS) {
drm_crtc_handle_vblank(&priv->pipe.crtc);
writel(reg, priv->base + CRT_CTRL1);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int aspeed_gfx_load(struct drm_device *drm)
{
struct platform_device *pdev = to_platform_device(drm->dev);
struct aspeed_gfx *priv;
struct resource *res;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
drm->dev_private = priv;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(drm->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
if (IS_ERR(priv->scu)) {
dev_err(&pdev->dev, "failed to find SCU regmap\n");
return PTR_ERR(priv->scu);
}
ret = of_reserved_mem_device_init(drm->dev);
if (ret) {
dev_err(&pdev->dev,
"failed to initialize reserved mem: %d\n", ret);
return ret;
}
ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret);
return ret;
}
priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(priv->rst)) {
dev_err(&pdev->dev,
"missing or invalid reset controller device tree entry");
return PTR_ERR(priv->rst);
}
reset_control_deassert(priv->rst);
priv->clk = devm_clk_get(drm->dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev,
"missing or invalid clk device tree entry");
return PTR_ERR(priv->clk);
}
clk_prepare_enable(priv->clk);
/* Sanitize control registers */
writel(0, priv->base + CRT_CTRL1);
writel(0, priv->base + CRT_CTRL2);
aspeed_gfx_setup_mode_config(drm);
ret = drm_vblank_init(drm, 1);
if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
return ret;
}
ret = aspeed_gfx_create_output(drm);
if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
return ret;
}
ret = aspeed_gfx_create_pipe(drm);
if (ret < 0) {
dev_err(drm->dev, "Cannot setup simple display pipe\n");
return ret;
}
ret = devm_request_irq(drm->dev, platform_get_irq(pdev, 0),
aspeed_gfx_irq_handler, 0, "aspeed gfx", drm);
if (ret < 0) {
dev_err(drm->dev, "Failed to install IRQ handler\n");
return ret;
}
drm_mode_config_reset(drm);
drm_fbdev_generic_setup(drm, 32);
return 0;
}
static void aspeed_gfx_unload(struct drm_device *drm)
{
drm_kms_helper_poll_fini(drm);
drm_mode_config_cleanup(drm);
drm->dev_private = NULL;
}
DEFINE_DRM_GEM_CMA_FOPS(fops);
static struct drm_driver aspeed_gfx_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET |
DRIVER_PRIME | DRIVER_ATOMIC,
.gem_create_object = drm_cma_gem_create_object_default_funcs,
.dumb_create = drm_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_import_sg_table = drm_gem_cma_prime_import_sg_table,
.gem_prime_mmap = drm_gem_prime_mmap,
.fops = &fops,
.name = "aspeed-gfx-drm",
.desc = "ASPEED GFX DRM",
.date = "20180319",
.major = 1,
.minor = 0,
};
static const struct of_device_id aspeed_gfx_match[] = {
{ .compatible = "aspeed,ast2500-gfx" },
{ }
};
static int aspeed_gfx_probe(struct platform_device *pdev)
{
struct drm_device *drm;
int ret;
drm = drm_dev_alloc(&aspeed_gfx_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
ret = aspeed_gfx_load(drm);
if (ret)
goto err_free;
ret = drm_dev_register(drm, 0);
if (ret)
goto err_unload;
return 0;
err_unload:
aspeed_gfx_unload(drm);
err_free:
drm_dev_put(drm);
return ret;
}
static int aspeed_gfx_remove(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
drm_dev_unregister(drm);
aspeed_gfx_unload(drm);
drm_dev_put(drm);
return 0;
}
static struct platform_driver aspeed_gfx_platform_driver = {
.probe = aspeed_gfx_probe,
.remove = aspeed_gfx_remove,
.driver = {
.name = "aspeed_gfx",
.of_match_table = aspeed_gfx_match,
},
};
module_platform_driver(aspeed_gfx_platform_driver);
MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2018 IBM Corporation
#include <drm/drm_atomic_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_probe_helper.h>
#include "aspeed_gfx.h"
static int aspeed_gfx_get_modes(struct drm_connector *connector)
{
return drm_add_modes_noedid(connector, 800, 600);
}
static const struct
drm_connector_helper_funcs aspeed_gfx_connector_helper_funcs = {
.get_modes = aspeed_gfx_get_modes,
};
static const struct drm_connector_funcs aspeed_gfx_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
int aspeed_gfx_create_output(struct drm_device *drm)
{
struct aspeed_gfx *priv = drm->dev_private;
int ret;
priv->connector.dpms = DRM_MODE_DPMS_OFF;
priv->connector.polled = 0;
drm_connector_helper_add(&priv->connector,
&aspeed_gfx_connector_helper_funcs);
ret = drm_connector_init(drm, &priv->connector,
&aspeed_gfx_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
return ret;
}
......@@ -73,7 +73,6 @@ struct bochs_device {
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
bool mode_config_initialized;
/* ttm */
struct {
......
......@@ -267,7 +267,6 @@ const struct drm_mode_config_funcs bochs_mode_funcs = {
int bochs_kms_init(struct bochs_device *bochs)
{
drm_mode_config_init(bochs->dev);
bochs->mode_config_initialized = true;
bochs->dev->mode_config.max_width = 8192;
bochs->dev->mode_config.max_height = 8192;
......@@ -292,8 +291,6 @@ int bochs_kms_init(struct bochs_device *bochs)
void bochs_kms_fini(struct bochs_device *bochs)
{
if (bochs->mode_config_initialized) {
drm_mode_config_cleanup(bochs->dev);
bochs->mode_config_initialized = false;
}
drm_atomic_helper_shutdown(bochs->dev);
drm_mode_config_cleanup(bochs->dev);
}
......@@ -101,7 +101,6 @@ struct cirrus_crtc {
struct cirrus_fbdev;
struct cirrus_mode_info {
bool mode_config_initialized;
struct cirrus_crtc *crtc;
/* pointer to fbdev info structure */
struct cirrus_fbdev *gfbdev;
......
......@@ -575,7 +575,6 @@ int cirrus_modeset_init(struct cirrus_device *cdev)
int ret;
drm_mode_config_init(cdev->dev);
cdev->mode_info.mode_config_initialized = true;
cdev->dev->mode_config.max_width = CIRRUS_MAX_FB_WIDTH;
cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT;
......@@ -613,9 +612,6 @@ int cirrus_modeset_init(struct cirrus_device *cdev)
void cirrus_modeset_fini(struct cirrus_device *cdev)
{
cirrus_fbdev_fini(cdev);
if (cdev->mode_info.mode_config_initialized) {
drm_mode_config_cleanup(cdev->dev);
cdev->mode_info.mode_config_initialized = false;
}
drm_helper_force_disable_all(cdev->dev);
drm_mode_config_cleanup(cdev->dev);
}
......@@ -639,20 +639,19 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)
{
struct drm_device *dev = fb_helper->dev;
struct drm_crtc *crtc;
struct drm_connector *connector;
struct drm_mode_set *modeset;
int i, j;
drm_modeset_lock_all(dev);
for (i = 0; i < fb_helper->crtc_count; i++) {
crtc = fb_helper->crtc_info[i].mode_set.crtc;
modeset = &fb_helper->crtc_info[i].mode_set;
if (!crtc->enabled)
if (!modeset->crtc->enabled)
continue;
/* Walk the connectors & encoders on this fb turning them on/off */
drm_fb_helper_for_each_connector(fb_helper, j) {
connector = fb_helper->connector_info[j]->connector;
for (j = 0; j < modeset->num_connectors; j++) {
connector = modeset->connectors[j];
connector->funcs->dpms(connector, dpms_mode);
drm_object_property_set_value(&connector->base,
dev->mode_config.dpms_property, dpms_mode);
......@@ -1874,7 +1873,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
int crtc_count = 0;
int i;
struct drm_fb_helper_surface_size sizes;
int gamma_size = 0;
int best_depth = 0;
memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
......@@ -1890,7 +1888,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
if (preferred_bpp != sizes.surface_bpp)
sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
/* first up get a count of crtcs now in use and new min/maxes width/heights */
drm_fb_helper_for_each_connector(fb_helper, i) {
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
struct drm_cmdline_mode *cmdline_mode;
......@@ -1970,6 +1967,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
sizes.surface_depth = best_depth;
}
/* first up get a count of crtcs now in use and new min/maxes width/heights */
crtc_count = 0;
for (i = 0; i < fb_helper->crtc_count; i++) {
struct drm_display_mode *desired_mode;
......@@ -1992,9 +1990,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
x = fb_helper->crtc_info[i].x;
y = fb_helper->crtc_info[i].y;
if (gamma_size == 0)
gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
......@@ -3317,8 +3312,6 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
return ret;
}
drm_client_add(&fb_helper->client);
if (!preferred_bpp)
preferred_bpp = dev->mode_config.preferred_depth;
if (!preferred_bpp)
......@@ -3329,6 +3322,8 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
if (ret)
DRM_DEV_DEBUG(dev->dev, "client hotplug ret=%d\n", ret);
drm_client_add(&fb_helper->client);
return 0;
}
EXPORT_SYMBOL(drm_fbdev_generic_setup);
......
......@@ -408,7 +408,7 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
loff_t num_pages = obj->size >> PAGE_SHIFT;
struct page *page;
if (vmf->pgoff > num_pages || WARN_ON_ONCE(!shmem->pages))
if (vmf->pgoff >= num_pages || WARN_ON_ONCE(!shmem->pages))
return VM_FAULT_SIGBUS;
page = shmem->pages[vmf->pgoff];
......
......@@ -180,12 +180,20 @@ int drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private);
/* drm_framebuffer.c */
void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
......
......@@ -686,12 +686,20 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_TRANSFER, drm_syncobj_transfer_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_WAIT, drm_syncobj_wait_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, drm_syncobj_timeline_wait_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_RESET, drm_syncobj_reset_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_SIGNAL, drm_syncobj_signal_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, drm_syncobj_timeline_signal_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_QUERY, drm_syncobj_query_ioctl,
DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_UNLOCKED),
......
......@@ -168,6 +168,13 @@ bool drm_need_swiotlb(int dma_bits)
if (xen_pv_domain())
return true;
/*
* Enforce dma_alloc_coherent when memory encryption is active as well
* for the same reasons as for Xen paravirtual hosts.
*/
if (mem_encrypt_active())
return true;
for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) {
max_iomem = max(max_iomem, tmp->end);
}
......
......@@ -253,3 +253,31 @@ void drm_err(const char *format, ...)
va_end(args);
}
EXPORT_SYMBOL(drm_err);
/**
* drm_print_regset32 - print the contents of registers to a
* &drm_printer stream.
*
* @p: the &drm printer
* @regset: the list of registers to print.
*
* Often in driver debug, it's useful to be able to either capture the
* contents of registers in the steady state using debugfs or at
* specific points during operation. This lets the driver have a
* single list of registers for both.
*/
void drm_print_regset32(struct drm_printer *p, struct debugfs_regset32 *regset)
{
int namelen = 0;
int i;
for (i = 0; i < regset->nregs; i++)
namelen = max(namelen, (int)strlen(regset->regs[i].name));
for (i = 0; i < regset->nregs; i++) {
drm_printf(p, "%*s = 0x%08x\n",
namelen, regset->regs[i].name,
readl(regset->base + regset->regs[i].offset));
}
}
EXPORT_SYMBOL(drm_print_regset32);
This diff is collapsed.
# SPDX-License-Identifier: GPL-2.0 OR MIT
# Copyright 2017-2019 Qiang Yu <yuq825@gmail.com>
config DRM_LIMA
tristate "LIMA (DRM support for ARM Mali 400/450 GPU)"
depends on DRM
depends on ARM || ARM64 || COMPILE_TEST
select DRM_SCHED
help
DRM driver for ARM Mali 400/450 GPUs.
# SPDX-License-Identifier: GPL-2.0 OR MIT
# Copyright 2017-2019 Qiang Yu <yuq825@gmail.com>
lima-y := \
lima_drv.o \
lima_device.o \
lima_pmu.o \
lima_l2_cache.o \
lima_mmu.o \
lima_gp.o \
lima_pp.o \
lima_gem.o \
lima_vm.o \
lima_sched.o \
lima_ctx.o \
lima_gem_prime.o \
lima_dlbu.o \
lima_bcast.o \
lima_object.o
obj-$(CONFIG_DRM_LIMA) += lima.o
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/io.h>
#include <linux/device.h>
#include "lima_device.h"
#include "lima_bcast.h"
#include "lima_regs.h"
#define bcast_write(reg, data) writel(data, ip->iomem + reg)
#define bcast_read(reg) readl(ip->iomem + reg)
void lima_bcast_enable(struct lima_device *dev, int num_pp)
{
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_pp;
struct lima_ip *ip = dev->ip + lima_ip_bcast;
int i, mask = bcast_read(LIMA_BCAST_BROADCAST_MASK) & 0xffff0000;
for (i = 0; i < num_pp; i++) {
struct lima_ip *pp = pipe->processor[i];
mask |= 1 << (pp->id - lima_ip_pp0);
}
bcast_write(LIMA_BCAST_BROADCAST_MASK, mask);
}
int lima_bcast_init(struct lima_ip *ip)
{
int i, mask = 0;
for (i = lima_ip_pp0; i <= lima_ip_pp7; i++) {
if (ip->dev->ip[i].present)
mask |= 1 << (i - lima_ip_pp0);
}
bcast_write(LIMA_BCAST_BROADCAST_MASK, mask << 16);
bcast_write(LIMA_BCAST_INTERRUPT_MASK, mask);
return 0;
}
void lima_bcast_fini(struct lima_ip *ip)
{
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_BCAST_H__
#define __LIMA_BCAST_H__
struct lima_ip;
int lima_bcast_init(struct lima_ip *ip);
void lima_bcast_fini(struct lima_ip *ip);
void lima_bcast_enable(struct lima_device *dev, int num_pp);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/slab.h>
#include "lima_device.h"
#include "lima_ctx.h"
int lima_ctx_create(struct lima_device *dev, struct lima_ctx_mgr *mgr, u32 *id)
{
struct lima_ctx *ctx;
int i, err;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->dev = dev;
kref_init(&ctx->refcnt);
for (i = 0; i < lima_pipe_num; i++) {
err = lima_sched_context_init(dev->pipe + i, ctx->context + i, &ctx->guilty);
if (err)
goto err_out0;
}
err = xa_alloc(&mgr->handles, id, ctx, xa_limit_32b, GFP_KERNEL);
if (err < 0)
goto err_out0;
return 0;
err_out0:
for (i--; i >= 0; i--)
lima_sched_context_fini(dev->pipe + i, ctx->context + i);
kfree(ctx);
return err;
}
static void lima_ctx_do_release(struct kref *ref)
{
struct lima_ctx *ctx = container_of(ref, struct lima_ctx, refcnt);
int i;
for (i = 0; i < lima_pipe_num; i++)
lima_sched_context_fini(ctx->dev->pipe + i, ctx->context + i);
kfree(ctx);
}
int lima_ctx_free(struct lima_ctx_mgr *mgr, u32 id)
{
struct lima_ctx *ctx;
int ret = 0;
mutex_lock(&mgr->lock);
ctx = xa_erase(&mgr->handles, id);
if (ctx)
kref_put(&ctx->refcnt, lima_ctx_do_release);
else
ret = -EINVAL;
mutex_unlock(&mgr->lock);
return ret;
}
struct lima_ctx *lima_ctx_get(struct lima_ctx_mgr *mgr, u32 id)
{
struct lima_ctx *ctx;
mutex_lock(&mgr->lock);
ctx = xa_load(&mgr->handles, id);
if (ctx)
kref_get(&ctx->refcnt);
mutex_unlock(&mgr->lock);
return ctx;
}
void lima_ctx_put(struct lima_ctx *ctx)
{
kref_put(&ctx->refcnt, lima_ctx_do_release);
}
void lima_ctx_mgr_init(struct lima_ctx_mgr *mgr)
{
mutex_init(&mgr->lock);
xa_init_flags(&mgr->handles, XA_FLAGS_ALLOC);
}
void lima_ctx_mgr_fini(struct lima_ctx_mgr *mgr)
{
struct lima_ctx *ctx;
unsigned long id;
xa_for_each(&mgr->handles, id, ctx) {
kref_put(&ctx->refcnt, lima_ctx_do_release);
}
xa_destroy(&mgr->handles);
mutex_destroy(&mgr->lock);
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_CTX_H__
#define __LIMA_CTX_H__
#include <linux/xarray.h>
#include "lima_device.h"
struct lima_ctx {
struct kref refcnt;
struct lima_device *dev;
struct lima_sched_context context[lima_pipe_num];
atomic_t guilty;
};
struct lima_ctx_mgr {
struct mutex lock;
struct xarray handles;
};
int lima_ctx_create(struct lima_device *dev, struct lima_ctx_mgr *mgr, u32 *id);
int lima_ctx_free(struct lima_ctx_mgr *mgr, u32 id);
struct lima_ctx *lima_ctx_get(struct lima_ctx_mgr *mgr, u32 id);
void lima_ctx_put(struct lima_ctx *ctx);
void lima_ctx_mgr_init(struct lima_ctx_mgr *mgr);
void lima_ctx_mgr_fini(struct lima_ctx_mgr *mgr);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include "lima_device.h"
#include "lima_gp.h"
#include "lima_pp.h"
#include "lima_mmu.h"
#include "lima_pmu.h"
#include "lima_l2_cache.h"
#include "lima_dlbu.h"
#include "lima_bcast.h"
#include "lima_vm.h"
struct lima_ip_desc {
char *name;
char *irq_name;
bool must_have[lima_gpu_num];
int offset[lima_gpu_num];
int (*init)(struct lima_ip *ip);
void (*fini)(struct lima_ip *ip);
};
#define LIMA_IP_DESC(ipname, mst0, mst1, off0, off1, func, irq) \
[lima_ip_##ipname] = { \
.name = #ipname, \
.irq_name = irq, \
.must_have = { \
[lima_gpu_mali400] = mst0, \
[lima_gpu_mali450] = mst1, \
}, \
.offset = { \
[lima_gpu_mali400] = off0, \
[lima_gpu_mali450] = off1, \
}, \
.init = lima_##func##_init, \
.fini = lima_##func##_fini, \
}
static struct lima_ip_desc lima_ip_desc[lima_ip_num] = {
LIMA_IP_DESC(pmu, false, false, 0x02000, 0x02000, pmu, "pmu"),
LIMA_IP_DESC(l2_cache0, true, true, 0x01000, 0x10000, l2_cache, NULL),
LIMA_IP_DESC(l2_cache1, false, true, -1, 0x01000, l2_cache, NULL),
LIMA_IP_DESC(l2_cache2, false, false, -1, 0x11000, l2_cache, NULL),
LIMA_IP_DESC(gp, true, true, 0x00000, 0x00000, gp, "gp"),
LIMA_IP_DESC(pp0, true, true, 0x08000, 0x08000, pp, "pp0"),
LIMA_IP_DESC(pp1, false, false, 0x0A000, 0x0A000, pp, "pp1"),
LIMA_IP_DESC(pp2, false, false, 0x0C000, 0x0C000, pp, "pp2"),
LIMA_IP_DESC(pp3, false, false, 0x0E000, 0x0E000, pp, "pp3"),
LIMA_IP_DESC(pp4, false, false, -1, 0x28000, pp, "pp4"),
LIMA_IP_DESC(pp5, false, false, -1, 0x2A000, pp, "pp5"),
LIMA_IP_DESC(pp6, false, false, -1, 0x2C000, pp, "pp6"),
LIMA_IP_DESC(pp7, false, false, -1, 0x2E000, pp, "pp7"),
LIMA_IP_DESC(gpmmu, true, true, 0x03000, 0x03000, mmu, "gpmmu"),
LIMA_IP_DESC(ppmmu0, true, true, 0x04000, 0x04000, mmu, "ppmmu0"),
LIMA_IP_DESC(ppmmu1, false, false, 0x05000, 0x05000, mmu, "ppmmu1"),
LIMA_IP_DESC(ppmmu2, false, false, 0x06000, 0x06000, mmu, "ppmmu2"),
LIMA_IP_DESC(ppmmu3, false, false, 0x07000, 0x07000, mmu, "ppmmu3"),
LIMA_IP_DESC(ppmmu4, false, false, -1, 0x1C000, mmu, "ppmmu4"),
LIMA_IP_DESC(ppmmu5, false, false, -1, 0x1D000, mmu, "ppmmu5"),
LIMA_IP_DESC(ppmmu6, false, false, -1, 0x1E000, mmu, "ppmmu6"),
LIMA_IP_DESC(ppmmu7, false, false, -1, 0x1F000, mmu, "ppmmu7"),
LIMA_IP_DESC(dlbu, false, true, -1, 0x14000, dlbu, NULL),
LIMA_IP_DESC(bcast, false, true, -1, 0x13000, bcast, NULL),
LIMA_IP_DESC(pp_bcast, false, true, -1, 0x16000, pp_bcast, "pp"),
LIMA_IP_DESC(ppmmu_bcast, false, true, -1, 0x15000, mmu, NULL),
};
const char *lima_ip_name(struct lima_ip *ip)
{
return lima_ip_desc[ip->id].name;
}
static int lima_clk_init(struct lima_device *dev)
{
int err;
unsigned long bus_rate, gpu_rate;
dev->clk_bus = devm_clk_get(dev->dev, "bus");
if (IS_ERR(dev->clk_bus)) {
dev_err(dev->dev, "get bus clk failed %ld\n", PTR_ERR(dev->clk_bus));
return PTR_ERR(dev->clk_bus);
}
dev->clk_gpu = devm_clk_get(dev->dev, "core");
if (IS_ERR(dev->clk_gpu)) {
dev_err(dev->dev, "get core clk failed %ld\n", PTR_ERR(dev->clk_gpu));
return PTR_ERR(dev->clk_gpu);
}
bus_rate = clk_get_rate(dev->clk_bus);
dev_info(dev->dev, "bus rate = %lu\n", bus_rate);
gpu_rate = clk_get_rate(dev->clk_gpu);
dev_info(dev->dev, "mod rate = %lu", gpu_rate);
err = clk_prepare_enable(dev->clk_bus);
if (err)
return err;
err = clk_prepare_enable(dev->clk_gpu);
if (err)
goto error_out0;
dev->reset = devm_reset_control_get_optional(dev->dev, NULL);
if (IS_ERR(dev->reset)) {
err = PTR_ERR(dev->reset);
goto error_out1;
} else if (dev->reset != NULL) {
err = reset_control_deassert(dev->reset);
if (err)
goto error_out1;
}
return 0;
error_out1:
clk_disable_unprepare(dev->clk_gpu);
error_out0:
clk_disable_unprepare(dev->clk_bus);
return err;
}
static void lima_clk_fini(struct lima_device *dev)
{
if (dev->reset != NULL)
reset_control_assert(dev->reset);
clk_disable_unprepare(dev->clk_gpu);
clk_disable_unprepare(dev->clk_bus);
}
static int lima_regulator_init(struct lima_device *dev)
{
int ret;
dev->regulator = devm_regulator_get_optional(dev->dev, "mali");
if (IS_ERR(dev->regulator)) {
ret = PTR_ERR(dev->regulator);
dev->regulator = NULL;
if (ret == -ENODEV)
return 0;
dev_err(dev->dev, "failed to get regulator: %d\n", ret);
return ret;
}
ret = regulator_enable(dev->regulator);
if (ret < 0) {
dev_err(dev->dev, "failed to enable regulator: %d\n", ret);
return ret;
}
return 0;
}
static void lima_regulator_fini(struct lima_device *dev)
{
if (dev->regulator)
regulator_disable(dev->regulator);
}
static int lima_init_ip(struct lima_device *dev, int index)
{
struct lima_ip_desc *desc = lima_ip_desc + index;
struct lima_ip *ip = dev->ip + index;
int offset = desc->offset[dev->id];
bool must = desc->must_have[dev->id];
int err;
if (offset < 0)
return 0;
ip->dev = dev;
ip->id = index;
ip->iomem = dev->iomem + offset;
if (desc->irq_name) {
err = platform_get_irq_byname(dev->pdev, desc->irq_name);
if (err < 0)
goto out;
ip->irq = err;
}
err = desc->init(ip);
if (!err) {
ip->present = true;
return 0;
}
out:
return must ? err : 0;
}
static void lima_fini_ip(struct lima_device *ldev, int index)
{
struct lima_ip_desc *desc = lima_ip_desc + index;
struct lima_ip *ip = ldev->ip + index;
if (ip->present)
desc->fini(ip);
}
static int lima_init_gp_pipe(struct lima_device *dev)
{
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp;
int err;
err = lima_sched_pipe_init(pipe, "gp");
if (err)
return err;
pipe->l2_cache[pipe->num_l2_cache++] = dev->ip + lima_ip_l2_cache0;
pipe->mmu[pipe->num_mmu++] = dev->ip + lima_ip_gpmmu;
pipe->processor[pipe->num_processor++] = dev->ip + lima_ip_gp;
err = lima_gp_pipe_init(dev);
if (err) {
lima_sched_pipe_fini(pipe);
return err;
}
return 0;
}
static void lima_fini_gp_pipe(struct lima_device *dev)
{
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp;
lima_gp_pipe_fini(dev);
lima_sched_pipe_fini(pipe);
}
static int lima_init_pp_pipe(struct lima_device *dev)
{
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_pp;
int err, i;
err = lima_sched_pipe_init(pipe, "pp");
if (err)
return err;
for (i = 0; i < LIMA_SCHED_PIPE_MAX_PROCESSOR; i++) {
struct lima_ip *pp = dev->ip + lima_ip_pp0 + i;
struct lima_ip *ppmmu = dev->ip + lima_ip_ppmmu0 + i;
struct lima_ip *l2_cache;
if (dev->id == lima_gpu_mali400)
l2_cache = dev->ip + lima_ip_l2_cache0;
else
l2_cache = dev->ip + lima_ip_l2_cache1 + (i >> 2);
if (pp->present && ppmmu->present && l2_cache->present) {
pipe->mmu[pipe->num_mmu++] = ppmmu;
pipe->processor[pipe->num_processor++] = pp;
if (!pipe->l2_cache[i >> 2])
pipe->l2_cache[pipe->num_l2_cache++] = l2_cache;
}
}
if (dev->ip[lima_ip_bcast].present) {
pipe->bcast_processor = dev->ip + lima_ip_pp_bcast;
pipe->bcast_mmu = dev->ip + lima_ip_ppmmu_bcast;
}
err = lima_pp_pipe_init(dev);
if (err) {
lima_sched_pipe_fini(pipe);
return err;
}
return 0;
}
static void lima_fini_pp_pipe(struct lima_device *dev)
{
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_pp;
lima_pp_pipe_fini(dev);
lima_sched_pipe_fini(pipe);
}
int lima_device_init(struct lima_device *ldev)
{
int err, i;
struct resource *res;
dma_set_coherent_mask(ldev->dev, DMA_BIT_MASK(32));
err = lima_clk_init(ldev);
if (err) {
dev_err(ldev->dev, "clk init fail %d\n", err);
return err;
}
err = lima_regulator_init(ldev);
if (err) {
dev_err(ldev->dev, "regulator init fail %d\n", err);
goto err_out0;
}
ldev->empty_vm = lima_vm_create(ldev);
if (!ldev->empty_vm) {
err = -ENOMEM;
goto err_out1;
}
ldev->va_start = 0;
if (ldev->id == lima_gpu_mali450) {
ldev->va_end = LIMA_VA_RESERVE_START;
ldev->dlbu_cpu = dma_alloc_wc(
ldev->dev, LIMA_PAGE_SIZE,
&ldev->dlbu_dma, GFP_KERNEL);
if (!ldev->dlbu_cpu) {
err = -ENOMEM;
goto err_out2;
}
} else
ldev->va_end = LIMA_VA_RESERVE_END;
res = platform_get_resource(ldev->pdev, IORESOURCE_MEM, 0);
ldev->iomem = devm_ioremap_resource(ldev->dev, res);
if (IS_ERR(ldev->iomem)) {
dev_err(ldev->dev, "fail to ioremap iomem\n");
err = PTR_ERR(ldev->iomem);
goto err_out3;
}
for (i = 0; i < lima_ip_num; i++) {
err = lima_init_ip(ldev, i);
if (err)
goto err_out4;
}
err = lima_init_gp_pipe(ldev);
if (err)
goto err_out4;
err = lima_init_pp_pipe(ldev);
if (err)
goto err_out5;
return 0;
err_out5:
lima_fini_gp_pipe(ldev);
err_out4:
while (--i >= 0)
lima_fini_ip(ldev, i);
err_out3:
if (ldev->dlbu_cpu)
dma_free_wc(ldev->dev, LIMA_PAGE_SIZE,
ldev->dlbu_cpu, ldev->dlbu_dma);
err_out2:
lima_vm_put(ldev->empty_vm);
err_out1:
lima_regulator_fini(ldev);
err_out0:
lima_clk_fini(ldev);
return err;
}
void lima_device_fini(struct lima_device *ldev)
{
int i;
lima_fini_pp_pipe(ldev);
lima_fini_gp_pipe(ldev);
for (i = lima_ip_num - 1; i >= 0; i--)
lima_fini_ip(ldev, i);
if (ldev->dlbu_cpu)
dma_free_wc(ldev->dev, LIMA_PAGE_SIZE,
ldev->dlbu_cpu, ldev->dlbu_dma);
lima_vm_put(ldev->empty_vm);
lima_regulator_fini(ldev);
lima_clk_fini(ldev);
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_DEVICE_H__
#define __LIMA_DEVICE_H__
#include <drm/drm_device.h>
#include <linux/delay.h>
#include "lima_sched.h"
enum lima_gpu_id {
lima_gpu_mali400 = 0,
lima_gpu_mali450,
lima_gpu_num,
};
enum lima_ip_id {
lima_ip_pmu,
lima_ip_gpmmu,
lima_ip_ppmmu0,
lima_ip_ppmmu1,
lima_ip_ppmmu2,
lima_ip_ppmmu3,
lima_ip_ppmmu4,
lima_ip_ppmmu5,
lima_ip_ppmmu6,
lima_ip_ppmmu7,
lima_ip_gp,
lima_ip_pp0,
lima_ip_pp1,
lima_ip_pp2,
lima_ip_pp3,
lima_ip_pp4,
lima_ip_pp5,
lima_ip_pp6,
lima_ip_pp7,
lima_ip_l2_cache0,
lima_ip_l2_cache1,
lima_ip_l2_cache2,
lima_ip_dlbu,
lima_ip_bcast,
lima_ip_pp_bcast,
lima_ip_ppmmu_bcast,
lima_ip_num,
};
struct lima_device;
struct lima_ip {
struct lima_device *dev;
enum lima_ip_id id;
bool present;
void __iomem *iomem;
int irq;
union {
/* gp/pp */
bool async_reset;
/* l2 cache */
spinlock_t lock;
} data;
};
enum lima_pipe_id {
lima_pipe_gp,
lima_pipe_pp,
lima_pipe_num,
};
struct lima_device {
struct device *dev;
struct drm_device *ddev;
struct platform_device *pdev;
enum lima_gpu_id id;
u32 gp_version;
u32 pp_version;
int num_pp;
void __iomem *iomem;
struct clk *clk_bus;
struct clk *clk_gpu;
struct reset_control *reset;
struct regulator *regulator;
struct lima_ip ip[lima_ip_num];
struct lima_sched_pipe pipe[lima_pipe_num];
struct lima_vm *empty_vm;
uint64_t va_start;
uint64_t va_end;
u32 *dlbu_cpu;
dma_addr_t dlbu_dma;
};
static inline struct lima_device *
to_lima_dev(struct drm_device *dev)
{
return dev->dev_private;
}
int lima_device_init(struct lima_device *ldev);
void lima_device_fini(struct lima_device *ldev);
const char *lima_ip_name(struct lima_ip *ip);
typedef int (*lima_poll_func_t)(struct lima_ip *);
static inline int lima_poll_timeout(struct lima_ip *ip, lima_poll_func_t func,
int sleep_us, int timeout_us)
{
ktime_t timeout = ktime_add_us(ktime_get(), timeout_us);
might_sleep_if(sleep_us);
while (1) {
if (func(ip))
return 0;
if (timeout_us && ktime_compare(ktime_get(), timeout) > 0)
return -ETIMEDOUT;
if (sleep_us)
usleep_range((sleep_us >> 2) + 1, sleep_us);
}
return 0;
}
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/io.h>
#include <linux/device.h>
#include "lima_device.h"
#include "lima_dlbu.h"
#include "lima_vm.h"
#include "lima_regs.h"
#define dlbu_write(reg, data) writel(data, ip->iomem + reg)
#define dlbu_read(reg) readl(ip->iomem + reg)
void lima_dlbu_enable(struct lima_device *dev, int num_pp)
{
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_pp;
struct lima_ip *ip = dev->ip + lima_ip_dlbu;
int i, mask = 0;
for (i = 0; i < num_pp; i++) {
struct lima_ip *pp = pipe->processor[i];
mask |= 1 << (pp->id - lima_ip_pp0);
}
dlbu_write(LIMA_DLBU_PP_ENABLE_MASK, mask);
}
void lima_dlbu_disable(struct lima_device *dev)
{
struct lima_ip *ip = dev->ip + lima_ip_dlbu;
dlbu_write(LIMA_DLBU_PP_ENABLE_MASK, 0);
}
void lima_dlbu_set_reg(struct lima_ip *ip, u32 *reg)
{
dlbu_write(LIMA_DLBU_TLLIST_VBASEADDR, reg[0]);
dlbu_write(LIMA_DLBU_FB_DIM, reg[1]);
dlbu_write(LIMA_DLBU_TLLIST_CONF, reg[2]);
dlbu_write(LIMA_DLBU_START_TILE_POS, reg[3]);
}
int lima_dlbu_init(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
dlbu_write(LIMA_DLBU_MASTER_TLLIST_PHYS_ADDR, dev->dlbu_dma | 1);
dlbu_write(LIMA_DLBU_MASTER_TLLIST_VADDR, LIMA_VA_RESERVE_DLBU);
return 0;
}
void lima_dlbu_fini(struct lima_ip *ip)
{
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_DLBU_H__
#define __LIMA_DLBU_H__
struct lima_ip;
struct lima_device;
void lima_dlbu_enable(struct lima_device *dev, int num_pp);
void lima_dlbu_disable(struct lima_device *dev);
void lima_dlbu_set_reg(struct lima_ip *ip, u32 *reg);
int lima_dlbu_init(struct lima_ip *ip);
void lima_dlbu_fini(struct lima_ip *ip);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_drv.h>
#include <drm/drm_prime.h>
#include <drm/lima_drm.h>
#include "lima_drv.h"
#include "lima_gem.h"
#include "lima_gem_prime.h"
#include "lima_vm.h"
int lima_sched_timeout_ms;
MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms (0 = no timeout (default))");
module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444);
static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_get_param *args = data;
struct lima_device *ldev = to_lima_dev(dev);
if (args->pad)
return -EINVAL;
switch (args->param) {
case DRM_LIMA_PARAM_GPU_ID:
switch (ldev->id) {
case lima_gpu_mali400:
args->value = DRM_LIMA_PARAM_GPU_ID_MALI400;
break;
case lima_gpu_mali450:
args->value = DRM_LIMA_PARAM_GPU_ID_MALI450;
break;
default:
args->value = DRM_LIMA_PARAM_GPU_ID_UNKNOWN;
break;
}
break;
case DRM_LIMA_PARAM_NUM_PP:
args->value = ldev->pipe[lima_pipe_pp].num_processor;
break;
case DRM_LIMA_PARAM_GP_VERSION:
args->value = ldev->gp_version;
break;
case DRM_LIMA_PARAM_PP_VERSION:
args->value = ldev->pp_version;
break;
default:
return -EINVAL;
}
return 0;
}
static int lima_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_gem_create *args = data;
if (args->pad)
return -EINVAL;
if (args->flags)
return -EINVAL;
if (args->size == 0)
return -EINVAL;
return lima_gem_create_handle(dev, file, args->size, args->flags, &args->handle);
}
static int lima_ioctl_gem_info(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_gem_info *args = data;
return lima_gem_get_info(file, args->handle, &args->va, &args->offset);
}
static int lima_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_gem_submit *args = data;
struct lima_device *ldev = to_lima_dev(dev);
struct lima_drm_priv *priv = file->driver_priv;
struct drm_lima_gem_submit_bo *bos;
struct lima_sched_pipe *pipe;
struct lima_sched_task *task;
struct lima_ctx *ctx;
struct lima_submit submit = {0};
size_t size;
int err = 0;
if (args->pipe >= lima_pipe_num || args->nr_bos == 0)
return -EINVAL;
if (args->flags & ~(LIMA_SUBMIT_FLAG_EXPLICIT_FENCE))
return -EINVAL;
pipe = ldev->pipe + args->pipe;
if (args->frame_size != pipe->frame_size)
return -EINVAL;
bos = kvcalloc(args->nr_bos, sizeof(*submit.bos) + sizeof(*submit.lbos), GFP_KERNEL);
if (!bos)
return -ENOMEM;
size = args->nr_bos * sizeof(*submit.bos);
if (copy_from_user(bos, u64_to_user_ptr(args->bos), size)) {
err = -EFAULT;
goto out0;
}
task = kmem_cache_zalloc(pipe->task_slab, GFP_KERNEL);
if (!task) {
err = -ENOMEM;
goto out0;
}
task->frame = task + 1;
if (copy_from_user(task->frame, u64_to_user_ptr(args->frame), args->frame_size)) {
err = -EFAULT;
goto out1;
}
err = pipe->task_validate(pipe, task);
if (err)
goto out1;
ctx = lima_ctx_get(&priv->ctx_mgr, args->ctx);
if (!ctx) {
err = -ENOENT;
goto out1;
}
submit.pipe = args->pipe;
submit.bos = bos;
submit.lbos = (void *)bos + size;
submit.nr_bos = args->nr_bos;
submit.task = task;
submit.ctx = ctx;
submit.flags = args->flags;
submit.in_sync[0] = args->in_sync[0];
submit.in_sync[1] = args->in_sync[1];
submit.out_sync = args->out_sync;
err = lima_gem_submit(file, &submit);
lima_ctx_put(ctx);
out1:
if (err)
kmem_cache_free(pipe->task_slab, task);
out0:
kvfree(bos);
return err;
}
static int lima_ioctl_gem_wait(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_gem_wait *args = data;
if (args->op & ~(LIMA_GEM_WAIT_READ|LIMA_GEM_WAIT_WRITE))
return -EINVAL;
return lima_gem_wait(file, args->handle, args->op, args->timeout_ns);
}
static int lima_ioctl_ctx_create(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_ctx_create *args = data;
struct lima_drm_priv *priv = file->driver_priv;
struct lima_device *ldev = to_lima_dev(dev);
if (args->_pad)
return -EINVAL;
return lima_ctx_create(ldev, &priv->ctx_mgr, &args->id);
}
static int lima_ioctl_ctx_free(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_lima_ctx_create *args = data;
struct lima_drm_priv *priv = file->driver_priv;
if (args->_pad)
return -EINVAL;
return lima_ctx_free(&priv->ctx_mgr, args->id);
}
static int lima_drm_driver_open(struct drm_device *dev, struct drm_file *file)
{
int err;
struct lima_drm_priv *priv;
struct lima_device *ldev = to_lima_dev(dev);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->vm = lima_vm_create(ldev);
if (!priv->vm) {
err = -ENOMEM;
goto err_out0;
}
lima_ctx_mgr_init(&priv->ctx_mgr);
file->driver_priv = priv;
return 0;
err_out0:
kfree(priv);
return err;
}
static void lima_drm_driver_postclose(struct drm_device *dev, struct drm_file *file)
{
struct lima_drm_priv *priv = file->driver_priv;
lima_ctx_mgr_fini(&priv->ctx_mgr);
lima_vm_put(priv->vm);
kfree(priv);
}
static const struct drm_ioctl_desc lima_drm_driver_ioctls[] = {
DRM_IOCTL_DEF_DRV(LIMA_GET_PARAM, lima_ioctl_get_param, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(LIMA_GEM_CREATE, lima_ioctl_gem_create, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(LIMA_GEM_INFO, lima_ioctl_gem_info, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(LIMA_GEM_SUBMIT, lima_ioctl_gem_submit, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(LIMA_GEM_WAIT, lima_ioctl_gem_wait, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(LIMA_CTX_CREATE, lima_ioctl_ctx_create, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(LIMA_CTX_FREE, lima_ioctl_ctx_free, DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct file_operations lima_drm_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
#endif
.mmap = lima_gem_mmap,
};
static struct drm_driver lima_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_PRIME | DRIVER_SYNCOBJ,
.open = lima_drm_driver_open,
.postclose = lima_drm_driver_postclose,
.ioctls = lima_drm_driver_ioctls,
.num_ioctls = ARRAY_SIZE(lima_drm_driver_ioctls),
.fops = &lima_drm_driver_fops,
.gem_free_object_unlocked = lima_gem_free_object,
.gem_open_object = lima_gem_object_open,
.gem_close_object = lima_gem_object_close,
.gem_vm_ops = &lima_gem_vm_ops,
.name = "lima",
.desc = "lima DRM",
.date = "20190217",
.major = 1,
.minor = 0,
.patchlevel = 0,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import_sg_table = lima_gem_prime_import_sg_table,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.gem_prime_get_sg_table = lima_gem_prime_get_sg_table,
.gem_prime_mmap = lima_gem_prime_mmap,
};
static int lima_pdev_probe(struct platform_device *pdev)
{
struct lima_device *ldev;
struct drm_device *ddev;
int err;
err = lima_sched_slab_init();
if (err)
return err;
ldev = devm_kzalloc(&pdev->dev, sizeof(*ldev), GFP_KERNEL);
if (!ldev) {
err = -ENOMEM;
goto err_out0;
}
ldev->pdev = pdev;
ldev->dev = &pdev->dev;
ldev->id = (enum lima_gpu_id)of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, ldev);
/* Allocate and initialize the DRM device. */
ddev = drm_dev_alloc(&lima_drm_driver, &pdev->dev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);
ddev->dev_private = ldev;
ldev->ddev = ddev;
err = lima_device_init(ldev);
if (err) {
dev_err(&pdev->dev, "Fatal error during GPU init\n");
goto err_out1;
}
/*
* Register the DRM device with the core and the connectors with
* sysfs.
*/
err = drm_dev_register(ddev, 0);
if (err < 0)
goto err_out2;
return 0;
err_out2:
lima_device_fini(ldev);
err_out1:
drm_dev_put(ddev);
err_out0:
lima_sched_slab_fini();
return err;
}
static int lima_pdev_remove(struct platform_device *pdev)
{
struct lima_device *ldev = platform_get_drvdata(pdev);
struct drm_device *ddev = ldev->ddev;
drm_dev_unregister(ddev);
lima_device_fini(ldev);
drm_dev_put(ddev);
lima_sched_slab_fini();
return 0;
}
static const struct of_device_id dt_match[] = {
{ .compatible = "arm,mali-400", .data = (void *)lima_gpu_mali400 },
{ .compatible = "arm,mali-450", .data = (void *)lima_gpu_mali450 },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
static struct platform_driver lima_platform_driver = {
.probe = lima_pdev_probe,
.remove = lima_pdev_remove,
.driver = {
.name = "lima",
.of_match_table = dt_match,
},
};
static int __init lima_init(void)
{
return platform_driver_register(&lima_platform_driver);
}
module_init(lima_init);
static void __exit lima_exit(void)
{
platform_driver_unregister(&lima_platform_driver);
}
module_exit(lima_exit);
MODULE_AUTHOR("Lima Project Developers");
MODULE_DESCRIPTION("Lima DRM Driver");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_DRV_H__
#define __LIMA_DRV_H__
#include <drm/drm_file.h>
#include "lima_ctx.h"
extern int lima_sched_timeout_ms;
struct lima_vm;
struct lima_bo;
struct lima_sched_task;
struct drm_lima_gem_submit_bo;
struct lima_drm_priv {
struct lima_vm *vm;
struct lima_ctx_mgr ctx_mgr;
};
struct lima_submit {
struct lima_ctx *ctx;
int pipe;
u32 flags;
struct drm_lima_gem_submit_bo *bos;
struct lima_bo **lbos;
u32 nr_bos;
u32 in_sync[2];
u32 out_sync;
struct lima_sched_task *task;
};
static inline struct lima_drm_priv *
to_lima_drm_priv(struct drm_file *file)
{
return file->driver_priv;
}
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/sync_file.h>
#include <linux/pfn_t.h>
#include <drm/drm_file.h>
#include <drm/drm_syncobj.h>
#include <drm/drm_utils.h>
#include <drm/lima_drm.h>
#include "lima_drv.h"
#include "lima_gem.h"
#include "lima_gem_prime.h"
#include "lima_vm.h"
#include "lima_object.h"
int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
u32 size, u32 flags, u32 *handle)
{
int err;
struct lima_bo *bo;
struct lima_device *ldev = to_lima_dev(dev);
bo = lima_bo_create(ldev, size, flags, NULL, NULL);
if (IS_ERR(bo))
return PTR_ERR(bo);
err = drm_gem_handle_create(file, &bo->gem, handle);
/* drop reference from allocate - handle holds it now */
drm_gem_object_put_unlocked(&bo->gem);
return err;
}
void lima_gem_free_object(struct drm_gem_object *obj)
{
struct lima_bo *bo = to_lima_bo(obj);
if (!list_empty(&bo->va))
dev_err(obj->dev->dev, "lima gem free bo still has va\n");
lima_bo_destroy(bo);
}
int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file)
{
struct lima_bo *bo = to_lima_bo(obj);
struct lima_drm_priv *priv = to_lima_drm_priv(file);
struct lima_vm *vm = priv->vm;
return lima_vm_bo_add(vm, bo, true);
}
void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file)
{
struct lima_bo *bo = to_lima_bo(obj);
struct lima_drm_priv *priv = to_lima_drm_priv(file);
struct lima_vm *vm = priv->vm;
lima_vm_bo_del(vm, bo);
}
int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset)
{
struct drm_gem_object *obj;
struct lima_bo *bo;
struct lima_drm_priv *priv = to_lima_drm_priv(file);
struct lima_vm *vm = priv->vm;
int err;
obj = drm_gem_object_lookup(file, handle);
if (!obj)
return -ENOENT;
bo = to_lima_bo(obj);
*va = lima_vm_get_va(vm, bo);
err = drm_gem_create_mmap_offset(obj);
if (!err)
*offset = drm_vma_node_offset_addr(&obj->vma_node);
drm_gem_object_put_unlocked(obj);
return err;
}
static vm_fault_t lima_gem_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *obj = vma->vm_private_data;
struct lima_bo *bo = to_lima_bo(obj);
pfn_t pfn;
pgoff_t pgoff;
/* We don't use vmf->pgoff since that has the fake offset: */
pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV);
return vmf_insert_mixed(vma, vmf->address, pfn);
}
const struct vm_operations_struct lima_gem_vm_ops = {
.fault = lima_gem_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
void lima_set_vma_flags(struct vm_area_struct *vma)
{
pgprot_t prot = vm_get_page_prot(vma->vm_flags);
vma->vm_flags |= VM_MIXEDMAP;
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_page_prot = pgprot_writecombine(prot);
}
int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
ret = drm_gem_mmap(filp, vma);
if (ret)
return ret;
lima_set_vma_flags(vma);
return 0;
}
static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo,
bool write, bool explicit)
{
int err = 0;
if (!write) {
err = reservation_object_reserve_shared(bo->gem.resv, 1);
if (err)
return err;
}
/* explicit sync use user passed dep fence */
if (explicit)
return 0;
/* implicit sync use bo fence in resv obj */
if (write) {
unsigned nr_fences;
struct dma_fence **fences;
int i;
err = reservation_object_get_fences_rcu(
bo->gem.resv, NULL, &nr_fences, &fences);
if (err || !nr_fences)
return err;
for (i = 0; i < nr_fences; i++) {
err = lima_sched_task_add_dep(task, fences[i]);
if (err)
break;
}
/* for error case free remaining fences */
for ( ; i < nr_fences; i++)
dma_fence_put(fences[i]);
kfree(fences);
} else {
struct dma_fence *fence;
fence = reservation_object_get_excl_rcu(bo->gem.resv);
if (fence) {
err = lima_sched_task_add_dep(task, fence);
if (err)
dma_fence_put(fence);
}
}
return err;
}
static int lima_gem_lock_bos(struct lima_bo **bos, u32 nr_bos,
struct ww_acquire_ctx *ctx)
{
int i, ret = 0, contended, slow_locked = -1;
ww_acquire_init(ctx, &reservation_ww_class);
retry:
for (i = 0; i < nr_bos; i++) {
if (i == slow_locked) {
slow_locked = -1;
continue;
}
ret = ww_mutex_lock_interruptible(&bos[i]->gem.resv->lock, ctx);
if (ret < 0) {
contended = i;
goto err;
}
}
ww_acquire_done(ctx);
return 0;
err:
for (i--; i >= 0; i--)
ww_mutex_unlock(&bos[i]->gem.resv->lock);
if (slow_locked >= 0)
ww_mutex_unlock(&bos[slow_locked]->gem.resv->lock);
if (ret == -EDEADLK) {
/* we lost out in a seqno race, lock and retry.. */
ret = ww_mutex_lock_slow_interruptible(
&bos[contended]->gem.resv->lock, ctx);
if (!ret) {
slow_locked = contended;
goto retry;
}
}
ww_acquire_fini(ctx);
return ret;
}
static void lima_gem_unlock_bos(struct lima_bo **bos, u32 nr_bos,
struct ww_acquire_ctx *ctx)
{
int i;
for (i = 0; i < nr_bos; i++)
ww_mutex_unlock(&bos[i]->gem.resv->lock);
ww_acquire_fini(ctx);
}
static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit)
{
int i, err;
for (i = 0; i < ARRAY_SIZE(submit->in_sync); i++) {
struct dma_fence *fence = NULL;
if (!submit->in_sync[i])
continue;
err = drm_syncobj_find_fence(file, submit->in_sync[i],
0, 0, &fence);
if (err)
return err;
err = lima_sched_task_add_dep(submit->task, fence);
if (err) {
dma_fence_put(fence);
return err;
}
}
return 0;
}
int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
{
int i, err = 0;
struct ww_acquire_ctx ctx;
struct lima_drm_priv *priv = to_lima_drm_priv(file);
struct lima_vm *vm = priv->vm;
struct drm_syncobj *out_sync = NULL;
struct dma_fence *fence;
struct lima_bo **bos = submit->lbos;
if (submit->out_sync) {
out_sync = drm_syncobj_find(file, submit->out_sync);
if (!out_sync)
return -ENOENT;
}
for (i = 0; i < submit->nr_bos; i++) {
struct drm_gem_object *obj;
struct lima_bo *bo;
obj = drm_gem_object_lookup(file, submit->bos[i].handle);
if (!obj) {
err = -ENOENT;
goto err_out0;
}
bo = to_lima_bo(obj);
/* increase refcnt of gpu va map to prevent unmapped when executing,
* will be decreased when task done
*/
err = lima_vm_bo_add(vm, bo, false);
if (err) {
drm_gem_object_put_unlocked(obj);
goto err_out0;
}
bos[i] = bo;
}
err = lima_gem_lock_bos(bos, submit->nr_bos, &ctx);
if (err)
goto err_out0;
err = lima_sched_task_init(
submit->task, submit->ctx->context + submit->pipe,
bos, submit->nr_bos, vm);
if (err)
goto err_out1;
err = lima_gem_add_deps(file, submit);
if (err)
goto err_out2;
for (i = 0; i < submit->nr_bos; i++) {
err = lima_gem_sync_bo(
submit->task, bos[i],
submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE,
submit->flags & LIMA_SUBMIT_FLAG_EXPLICIT_FENCE);
if (err)
goto err_out2;
}
fence = lima_sched_context_queue_task(
submit->ctx->context + submit->pipe, submit->task);
for (i = 0; i < submit->nr_bos; i++) {
if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE)
reservation_object_add_excl_fence(bos[i]->gem.resv, fence);
else
reservation_object_add_shared_fence(bos[i]->gem.resv, fence);
}
lima_gem_unlock_bos(bos, submit->nr_bos, &ctx);
for (i = 0; i < submit->nr_bos; i++)
drm_gem_object_put_unlocked(&bos[i]->gem);
if (out_sync) {
drm_syncobj_replace_fence(out_sync, fence);
drm_syncobj_put(out_sync);
}
dma_fence_put(fence);
return 0;
err_out2:
lima_sched_task_fini(submit->task);
err_out1:
lima_gem_unlock_bos(bos, submit->nr_bos, &ctx);
err_out0:
for (i = 0; i < submit->nr_bos; i++) {
if (!bos[i])
break;
lima_vm_bo_del(vm, bos[i]);
drm_gem_object_put_unlocked(&bos[i]->gem);
}
if (out_sync)
drm_syncobj_put(out_sync);
return err;
}
int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, s64 timeout_ns)
{
bool write = op & LIMA_GEM_WAIT_WRITE;
long ret, timeout;
if (!op)
return 0;
timeout = drm_timeout_abs_to_jiffies(timeout_ns);
ret = drm_gem_reservation_object_wait(file, handle, write, timeout);
if (ret == 0)
ret = timeout ? -ETIMEDOUT : -EBUSY;
return ret;
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_GEM_H__
#define __LIMA_GEM_H__
struct lima_bo;
struct lima_submit;
extern const struct vm_operations_struct lima_gem_vm_ops;
struct lima_bo *lima_gem_create_bo(struct drm_device *dev, u32 size, u32 flags);
int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
u32 size, u32 flags, u32 *handle);
void lima_gem_free_object(struct drm_gem_object *obj);
int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file);
void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file);
int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset);
int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma);
int lima_gem_submit(struct drm_file *file, struct lima_submit *submit);
int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, s64 timeout_ns);
void lima_set_vma_flags(struct vm_area_struct *vma);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/dma-buf.h>
#include <drm/drm_prime.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include "lima_device.h"
#include "lima_object.h"
#include "lima_gem.h"
#include "lima_gem_prime.h"
struct drm_gem_object *lima_gem_prime_import_sg_table(
struct drm_device *dev, struct dma_buf_attachment *attach,
struct sg_table *sgt)
{
struct lima_device *ldev = to_lima_dev(dev);
struct lima_bo *bo;
bo = lima_bo_create(ldev, attach->dmabuf->size, 0, sgt,
attach->dmabuf->resv);
if (IS_ERR(bo))
return ERR_CAST(bo);
return &bo->gem;
}
struct sg_table *lima_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct lima_bo *bo = to_lima_bo(obj);
int npages = obj->size >> PAGE_SHIFT;
return drm_prime_pages_to_sg(bo->pages, npages);
}
int lima_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
int ret;
ret = drm_gem_mmap_obj(obj, obj->size, vma);
if (ret)
return ret;
lima_set_vma_flags(vma);
return 0;
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_GEM_PRIME_H__
#define __LIMA_GEM_PRIME_H__
struct drm_gem_object *lima_gem_prime_import_sg_table(
struct drm_device *dev, struct dma_buf_attachment *attach,
struct sg_table *sgt);
struct sg_table *lima_gem_prime_get_sg_table(struct drm_gem_object *obj);
int lima_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <drm/lima_drm.h>
#include "lima_device.h"
#include "lima_gp.h"
#include "lima_regs.h"
#define gp_write(reg, data) writel(data, ip->iomem + reg)
#define gp_read(reg) readl(ip->iomem + reg)
static irqreturn_t lima_gp_irq_handler(int irq, void *data)
{
struct lima_ip *ip = data;
struct lima_device *dev = ip->dev;
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp;
u32 state = gp_read(LIMA_GP_INT_STAT);
u32 status = gp_read(LIMA_GP_STATUS);
bool done = false;
/* for shared irq case */
if (!state)
return IRQ_NONE;
if (state & LIMA_GP_IRQ_MASK_ERROR) {
dev_err(dev->dev, "gp error irq state=%x status=%x\n",
state, status);
/* mask all interrupts before hard reset */
gp_write(LIMA_GP_INT_MASK, 0);
pipe->error = true;
done = true;
} else {
bool valid = state & (LIMA_GP_IRQ_VS_END_CMD_LST |
LIMA_GP_IRQ_PLBU_END_CMD_LST);
bool active = status & (LIMA_GP_STATUS_VS_ACTIVE |
LIMA_GP_STATUS_PLBU_ACTIVE);
done = valid && !active;
}
gp_write(LIMA_GP_INT_CLEAR, state);
if (done)
lima_sched_pipe_task_done(pipe);
return IRQ_HANDLED;
}
static void lima_gp_soft_reset_async(struct lima_ip *ip)
{
if (ip->data.async_reset)
return;
gp_write(LIMA_GP_INT_MASK, 0);
gp_write(LIMA_GP_INT_CLEAR, LIMA_GP_IRQ_RESET_COMPLETED);
gp_write(LIMA_GP_CMD, LIMA_GP_CMD_SOFT_RESET);
ip->data.async_reset = true;
}
static int lima_gp_soft_reset_async_wait(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
int err;
u32 v;
if (!ip->data.async_reset)
return 0;
err = readl_poll_timeout(ip->iomem + LIMA_GP_INT_RAWSTAT, v,
v & LIMA_GP_IRQ_RESET_COMPLETED,
0, 100);
if (err) {
dev_err(dev->dev, "gp soft reset time out\n");
return err;
}
gp_write(LIMA_GP_INT_CLEAR, LIMA_GP_IRQ_MASK_ALL);
gp_write(LIMA_GP_INT_MASK, LIMA_GP_IRQ_MASK_USED);
ip->data.async_reset = false;
return 0;
}
static int lima_gp_task_validate(struct lima_sched_pipe *pipe,
struct lima_sched_task *task)
{
struct drm_lima_gp_frame *frame = task->frame;
u32 *f = frame->frame;
(void)pipe;
if (f[LIMA_GP_VSCL_START_ADDR >> 2] >
f[LIMA_GP_VSCL_END_ADDR >> 2] ||
f[LIMA_GP_PLBUCL_START_ADDR >> 2] >
f[LIMA_GP_PLBUCL_END_ADDR >> 2] ||
f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2] >
f[LIMA_GP_PLBU_ALLOC_END_ADDR >> 2])
return -EINVAL;
if (f[LIMA_GP_VSCL_START_ADDR >> 2] ==
f[LIMA_GP_VSCL_END_ADDR >> 2] &&
f[LIMA_GP_PLBUCL_START_ADDR >> 2] ==
f[LIMA_GP_PLBUCL_END_ADDR >> 2])
return -EINVAL;
return 0;
}
static void lima_gp_task_run(struct lima_sched_pipe *pipe,
struct lima_sched_task *task)
{
struct lima_ip *ip = pipe->processor[0];
struct drm_lima_gp_frame *frame = task->frame;
u32 *f = frame->frame;
u32 cmd = 0;
int i;
if (f[LIMA_GP_VSCL_START_ADDR >> 2] !=
f[LIMA_GP_VSCL_END_ADDR >> 2])
cmd |= LIMA_GP_CMD_START_VS;
if (f[LIMA_GP_PLBUCL_START_ADDR >> 2] !=
f[LIMA_GP_PLBUCL_END_ADDR >> 2])
cmd |= LIMA_GP_CMD_START_PLBU;
/* before any hw ops, wait last success task async soft reset */
lima_gp_soft_reset_async_wait(ip);
for (i = 0; i < LIMA_GP_FRAME_REG_NUM; i++)
writel(f[i], ip->iomem + LIMA_GP_VSCL_START_ADDR + i * 4);
gp_write(LIMA_GP_CMD, LIMA_GP_CMD_UPDATE_PLBU_ALLOC);
gp_write(LIMA_GP_CMD, cmd);
}
static int lima_gp_hard_reset_poll(struct lima_ip *ip)
{
gp_write(LIMA_GP_PERF_CNT_0_LIMIT, 0xC01A0000);
return gp_read(LIMA_GP_PERF_CNT_0_LIMIT) == 0xC01A0000;
}
static int lima_gp_hard_reset(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
int ret;
gp_write(LIMA_GP_PERF_CNT_0_LIMIT, 0xC0FFE000);
gp_write(LIMA_GP_INT_MASK, 0);
gp_write(LIMA_GP_CMD, LIMA_GP_CMD_RESET);
ret = lima_poll_timeout(ip, lima_gp_hard_reset_poll, 10, 100);
if (ret) {
dev_err(dev->dev, "gp hard reset timeout\n");
return ret;
}
gp_write(LIMA_GP_PERF_CNT_0_LIMIT, 0);
gp_write(LIMA_GP_INT_CLEAR, LIMA_GP_IRQ_MASK_ALL);
gp_write(LIMA_GP_INT_MASK, LIMA_GP_IRQ_MASK_USED);
return 0;
}
static void lima_gp_task_fini(struct lima_sched_pipe *pipe)
{
lima_gp_soft_reset_async(pipe->processor[0]);
}
static void lima_gp_task_error(struct lima_sched_pipe *pipe)
{
struct lima_ip *ip = pipe->processor[0];
dev_err(ip->dev->dev, "gp task error int_state=%x status=%x\n",
gp_read(LIMA_GP_INT_STAT), gp_read(LIMA_GP_STATUS));
lima_gp_hard_reset(ip);
}
static void lima_gp_task_mmu_error(struct lima_sched_pipe *pipe)
{
lima_sched_pipe_task_done(pipe);
}
static void lima_gp_print_version(struct lima_ip *ip)
{
u32 version, major, minor;
char *name;
version = gp_read(LIMA_GP_VERSION);
major = (version >> 8) & 0xFF;
minor = version & 0xFF;
switch (version >> 16) {
case 0xA07:
name = "mali200";
break;
case 0xC07:
name = "mali300";
break;
case 0xB07:
name = "mali400";
break;
case 0xD07:
name = "mali450";
break;
default:
name = "unknown";
break;
}
dev_info(ip->dev->dev, "%s - %s version major %d minor %d\n",
lima_ip_name(ip), name, major, minor);
}
static struct kmem_cache *lima_gp_task_slab;
static int lima_gp_task_slab_refcnt;
int lima_gp_init(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
int err;
lima_gp_print_version(ip);
ip->data.async_reset = false;
lima_gp_soft_reset_async(ip);
err = lima_gp_soft_reset_async_wait(ip);
if (err)
return err;
err = devm_request_irq(dev->dev, ip->irq, lima_gp_irq_handler,
IRQF_SHARED, lima_ip_name(ip), ip);
if (err) {
dev_err(dev->dev, "gp %s fail to request irq\n",
lima_ip_name(ip));
return err;
}
dev->gp_version = gp_read(LIMA_GP_VERSION);
return 0;
}
void lima_gp_fini(struct lima_ip *ip)
{
}
int lima_gp_pipe_init(struct lima_device *dev)
{
int frame_size = sizeof(struct drm_lima_gp_frame);
struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp;
if (!lima_gp_task_slab) {
lima_gp_task_slab = kmem_cache_create_usercopy(
"lima_gp_task", sizeof(struct lima_sched_task) + frame_size,
0, SLAB_HWCACHE_ALIGN, sizeof(struct lima_sched_task),
frame_size, NULL);
if (!lima_gp_task_slab)
return -ENOMEM;
}
lima_gp_task_slab_refcnt++;
pipe->frame_size = frame_size;
pipe->task_slab = lima_gp_task_slab;
pipe->task_validate = lima_gp_task_validate;
pipe->task_run = lima_gp_task_run;
pipe->task_fini = lima_gp_task_fini;
pipe->task_error = lima_gp_task_error;
pipe->task_mmu_error = lima_gp_task_mmu_error;
return 0;
}
void lima_gp_pipe_fini(struct lima_device *dev)
{
if (!--lima_gp_task_slab_refcnt) {
kmem_cache_destroy(lima_gp_task_slab);
lima_gp_task_slab = NULL;
}
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_GP_H__
#define __LIMA_GP_H__
struct lima_ip;
struct lima_device;
int lima_gp_init(struct lima_ip *ip);
void lima_gp_fini(struct lima_ip *ip);
int lima_gp_pipe_init(struct lima_device *dev);
void lima_gp_pipe_fini(struct lima_device *dev);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/iopoll.h>
#include <linux/device.h>
#include "lima_device.h"
#include "lima_l2_cache.h"
#include "lima_regs.h"
#define l2_cache_write(reg, data) writel(data, ip->iomem + reg)
#define l2_cache_read(reg) readl(ip->iomem + reg)
static int lima_l2_cache_wait_idle(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
int err;
u32 v;
err = readl_poll_timeout(ip->iomem + LIMA_L2_CACHE_STATUS, v,
!(v & LIMA_L2_CACHE_STATUS_COMMAND_BUSY),
0, 1000);
if (err) {
dev_err(dev->dev, "l2 cache wait command timeout\n");
return err;
}
return 0;
}
int lima_l2_cache_flush(struct lima_ip *ip)
{
int ret;
spin_lock(&ip->data.lock);
l2_cache_write(LIMA_L2_CACHE_COMMAND, LIMA_L2_CACHE_COMMAND_CLEAR_ALL);
ret = lima_l2_cache_wait_idle(ip);
spin_unlock(&ip->data.lock);
return ret;
}
int lima_l2_cache_init(struct lima_ip *ip)
{
int i, err;
u32 size;
struct lima_device *dev = ip->dev;
/* l2_cache2 only exists when one of PP4-7 present */
if (ip->id == lima_ip_l2_cache2) {
for (i = lima_ip_pp4; i <= lima_ip_pp7; i++) {
if (dev->ip[i].present)
break;
}
if (i > lima_ip_pp7)
return -ENODEV;
}
spin_lock_init(&ip->data.lock);
size = l2_cache_read(LIMA_L2_CACHE_SIZE);
dev_info(dev->dev, "l2 cache %uK, %u-way, %ubyte cache line, %ubit external bus\n",
1 << (((size >> 16) & 0xff) - 10),
1 << ((size >> 8) & 0xff),
1 << (size & 0xff),
1 << ((size >> 24) & 0xff));
err = lima_l2_cache_flush(ip);
if (err)
return err;
l2_cache_write(LIMA_L2_CACHE_ENABLE,
LIMA_L2_CACHE_ENABLE_ACCESS|LIMA_L2_CACHE_ENABLE_READ_ALLOCATE);
l2_cache_write(LIMA_L2_CACHE_MAX_READS, 0x1c);
return 0;
}
void lima_l2_cache_fini(struct lima_ip *ip)
{
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_L2_CACHE_H__
#define __LIMA_L2_CACHE_H__
struct lima_ip;
int lima_l2_cache_init(struct lima_ip *ip);
void lima_l2_cache_fini(struct lima_ip *ip);
int lima_l2_cache_flush(struct lima_ip *ip);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/device.h>
#include "lima_device.h"
#include "lima_mmu.h"
#include "lima_vm.h"
#include "lima_object.h"
#include "lima_regs.h"
#define mmu_write(reg, data) writel(data, ip->iomem + reg)
#define mmu_read(reg) readl(ip->iomem + reg)
#define lima_mmu_send_command(cmd, addr, val, cond) \
({ \
int __ret; \
\
mmu_write(LIMA_MMU_COMMAND, cmd); \
__ret = readl_poll_timeout(ip->iomem + (addr), val, \
cond, 0, 100); \
if (__ret) \
dev_err(dev->dev, \
"mmu command %x timeout\n", cmd); \
__ret; \
})
static irqreturn_t lima_mmu_irq_handler(int irq, void *data)
{
struct lima_ip *ip = data;
struct lima_device *dev = ip->dev;
u32 status = mmu_read(LIMA_MMU_INT_STATUS);
struct lima_sched_pipe *pipe;
/* for shared irq case */
if (!status)
return IRQ_NONE;
if (status & LIMA_MMU_INT_PAGE_FAULT) {
u32 fault = mmu_read(LIMA_MMU_PAGE_FAULT_ADDR);
dev_err(dev->dev, "mmu page fault at 0x%x from bus id %d of type %s on %s\n",
fault, LIMA_MMU_STATUS_BUS_ID(status),
status & LIMA_MMU_STATUS_PAGE_FAULT_IS_WRITE ? "write" : "read",
lima_ip_name(ip));
}
if (status & LIMA_MMU_INT_READ_BUS_ERROR)
dev_err(dev->dev, "mmu %s irq bus error\n", lima_ip_name(ip));
/* mask all interrupts before resume */
mmu_write(LIMA_MMU_INT_MASK, 0);
mmu_write(LIMA_MMU_INT_CLEAR, status);
pipe = dev->pipe + (ip->id == lima_ip_gpmmu ? lima_pipe_gp : lima_pipe_pp);
lima_sched_pipe_mmu_error(pipe);
return IRQ_HANDLED;
}
int lima_mmu_init(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
int err;
u32 v;
if (ip->id == lima_ip_ppmmu_bcast)
return 0;
mmu_write(LIMA_MMU_DTE_ADDR, 0xCAFEBABE);
if (mmu_read(LIMA_MMU_DTE_ADDR) != 0xCAFEB000) {
dev_err(dev->dev, "mmu %s dte write test fail\n", lima_ip_name(ip));
return -EIO;
}
mmu_write(LIMA_MMU_COMMAND, LIMA_MMU_COMMAND_HARD_RESET);
err = lima_mmu_send_command(LIMA_MMU_COMMAND_HARD_RESET,
LIMA_MMU_DTE_ADDR, v, v == 0);
if (err)
return err;
err = devm_request_irq(dev->dev, ip->irq, lima_mmu_irq_handler,
IRQF_SHARED, lima_ip_name(ip), ip);
if (err) {
dev_err(dev->dev, "mmu %s fail to request irq\n", lima_ip_name(ip));
return err;
}
mmu_write(LIMA_MMU_INT_MASK, LIMA_MMU_INT_PAGE_FAULT | LIMA_MMU_INT_READ_BUS_ERROR);
mmu_write(LIMA_MMU_DTE_ADDR, dev->empty_vm->pd.dma);
return lima_mmu_send_command(LIMA_MMU_COMMAND_ENABLE_PAGING,
LIMA_MMU_STATUS, v,
v & LIMA_MMU_STATUS_PAGING_ENABLED);
}
void lima_mmu_fini(struct lima_ip *ip)
{
}
void lima_mmu_switch_vm(struct lima_ip *ip, struct lima_vm *vm)
{
struct lima_device *dev = ip->dev;
u32 v;
lima_mmu_send_command(LIMA_MMU_COMMAND_ENABLE_STALL,
LIMA_MMU_STATUS, v,
v & LIMA_MMU_STATUS_STALL_ACTIVE);
if (vm)
mmu_write(LIMA_MMU_DTE_ADDR, vm->pd.dma);
/* flush the TLB */
mmu_write(LIMA_MMU_COMMAND, LIMA_MMU_COMMAND_ZAP_CACHE);
lima_mmu_send_command(LIMA_MMU_COMMAND_DISABLE_STALL,
LIMA_MMU_STATUS, v,
!(v & LIMA_MMU_STATUS_STALL_ACTIVE));
}
void lima_mmu_page_fault_resume(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
u32 status = mmu_read(LIMA_MMU_STATUS);
u32 v;
if (status & LIMA_MMU_STATUS_PAGE_FAULT_ACTIVE) {
dev_info(dev->dev, "mmu resume\n");
mmu_write(LIMA_MMU_INT_MASK, 0);
mmu_write(LIMA_MMU_DTE_ADDR, 0xCAFEBABE);
lima_mmu_send_command(LIMA_MMU_COMMAND_HARD_RESET,
LIMA_MMU_DTE_ADDR, v, v == 0);
mmu_write(LIMA_MMU_INT_MASK, LIMA_MMU_INT_PAGE_FAULT | LIMA_MMU_INT_READ_BUS_ERROR);
mmu_write(LIMA_MMU_DTE_ADDR, dev->empty_vm->pd.dma);
lima_mmu_send_command(LIMA_MMU_COMMAND_ENABLE_PAGING,
LIMA_MMU_STATUS, v,
v & LIMA_MMU_STATUS_PAGING_ENABLED);
}
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_MMU_H__
#define __LIMA_MMU_H__
struct lima_ip;
struct lima_vm;
int lima_mmu_init(struct lima_ip *ip);
void lima_mmu_fini(struct lima_ip *ip);
void lima_mmu_switch_vm(struct lima_ip *ip, struct lima_vm *vm);
void lima_mmu_page_fault_resume(struct lima_ip *ip);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#include <drm/drm_prime.h>
#include <linux/pagemap.h>
#include <linux/dma-mapping.h>
#include "lima_object.h"
void lima_bo_destroy(struct lima_bo *bo)
{
if (bo->sgt) {
kfree(bo->pages);
drm_prime_gem_destroy(&bo->gem, bo->sgt);
} else {
if (bo->pages_dma_addr) {
int i, npages = bo->gem.size >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
if (bo->pages_dma_addr[i])
dma_unmap_page(bo->gem.dev->dev,
bo->pages_dma_addr[i],
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
}
if (bo->pages)
drm_gem_put_pages(&bo->gem, bo->pages, true, true);
}
kfree(bo->pages_dma_addr);
drm_gem_object_release(&bo->gem);
kfree(bo);
}
static struct lima_bo *lima_bo_create_struct(struct lima_device *dev, u32 size, u32 flags,
struct reservation_object *resv)
{
struct lima_bo *bo;
int err;
size = PAGE_ALIGN(size);
bo = kzalloc(sizeof(*bo), GFP_KERNEL);
if (!bo)
return ERR_PTR(-ENOMEM);
mutex_init(&bo->lock);
INIT_LIST_HEAD(&bo->va);
bo->gem.resv = resv;
err = drm_gem_object_init(dev->ddev, &bo->gem, size);
if (err) {
kfree(bo);
return ERR_PTR(err);
}
return bo;
}
struct lima_bo *lima_bo_create(struct lima_device *dev, u32 size,
u32 flags, struct sg_table *sgt,
struct reservation_object *resv)
{
int i, err;
size_t npages;
struct lima_bo *bo, *ret;
bo = lima_bo_create_struct(dev, size, flags, resv);
if (IS_ERR(bo))
return bo;
npages = bo->gem.size >> PAGE_SHIFT;
bo->pages_dma_addr = kcalloc(npages, sizeof(dma_addr_t), GFP_KERNEL);
if (!bo->pages_dma_addr) {
ret = ERR_PTR(-ENOMEM);
goto err_out;
}
if (sgt) {
bo->sgt = sgt;
bo->pages = kcalloc(npages, sizeof(*bo->pages), GFP_KERNEL);
if (!bo->pages) {
ret = ERR_PTR(-ENOMEM);
goto err_out;
}
err = drm_prime_sg_to_page_addr_arrays(
sgt, bo->pages, bo->pages_dma_addr, npages);
if (err) {
ret = ERR_PTR(err);
goto err_out;
}
} else {
mapping_set_gfp_mask(bo->gem.filp->f_mapping, GFP_DMA32);
bo->pages = drm_gem_get_pages(&bo->gem);
if (IS_ERR(bo->pages)) {
ret = ERR_CAST(bo->pages);
bo->pages = NULL;
goto err_out;
}
for (i = 0; i < npages; i++) {
dma_addr_t addr = dma_map_page(dev->dev, bo->pages[i], 0,
PAGE_SIZE, DMA_BIDIRECTIONAL);
if (dma_mapping_error(dev->dev, addr)) {
ret = ERR_PTR(-EFAULT);
goto err_out;
}
bo->pages_dma_addr[i] = addr;
}
}
return bo;
err_out:
lima_bo_destroy(bo);
return ret;
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_OBJECT_H__
#define __LIMA_OBJECT_H__
#include <drm/drm_gem.h>
#include "lima_device.h"
struct lima_bo {
struct drm_gem_object gem;
struct page **pages;
dma_addr_t *pages_dma_addr;
struct sg_table *sgt;
void *vaddr;
struct mutex lock;
struct list_head va;
};
static inline struct lima_bo *
to_lima_bo(struct drm_gem_object *obj)
{
return container_of(obj, struct lima_bo, gem);
}
struct lima_bo *lima_bo_create(struct lima_device *dev, u32 size,
u32 flags, struct sg_table *sgt,
struct reservation_object *resv);
void lima_bo_destroy(struct lima_bo *bo);
void *lima_bo_vmap(struct lima_bo *bo);
void lima_bo_vunmap(struct lima_bo *bo);
#endif
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/iopoll.h>
#include <linux/device.h>
#include "lima_device.h"
#include "lima_pmu.h"
#include "lima_regs.h"
#define pmu_write(reg, data) writel(data, ip->iomem + reg)
#define pmu_read(reg) readl(ip->iomem + reg)
static int lima_pmu_wait_cmd(struct lima_ip *ip)
{
struct lima_device *dev = ip->dev;
int err;
u32 v;
err = readl_poll_timeout(ip->iomem + LIMA_PMU_INT_RAWSTAT,
v, v & LIMA_PMU_INT_CMD_MASK,
100, 100000);
if (err) {
dev_err(dev->dev, "timeout wait pmd cmd\n");
return err;
}
pmu_write(LIMA_PMU_INT_CLEAR, LIMA_PMU_INT_CMD_MASK);
return 0;
}
int lima_pmu_init(struct lima_ip *ip)
{
int err;
u32 stat;
pmu_write(LIMA_PMU_INT_MASK, 0);
/* If this value is too low, when in high GPU clk freq,
* GPU will be in unstable state.
*/
pmu_write(LIMA_PMU_SW_DELAY, 0xffff);
/* status reg 1=off 0=on */
stat = pmu_read(LIMA_PMU_STATUS);
/* power up all ip */
if (stat) {
pmu_write(LIMA_PMU_POWER_UP, stat);
err = lima_pmu_wait_cmd(ip);
if (err)
return err;
}
return 0;
}
void lima_pmu_fini(struct lima_ip *ip)
{
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_PMU_H__
#define __LIMA_PMU_H__
struct lima_ip;
int lima_pmu_init(struct lima_ip *ip);
void lima_pmu_fini(struct lima_ip *ip);
#endif
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_PP_H__
#define __LIMA_PP_H__
struct lima_ip;
struct lima_device;
int lima_pp_init(struct lima_ip *ip);
void lima_pp_fini(struct lima_ip *ip);
int lima_pp_bcast_init(struct lima_ip *ip);
void lima_pp_bcast_fini(struct lima_ip *ip);
int lima_pp_pipe_init(struct lima_device *dev);
void lima_pp_pipe_fini(struct lima_device *dev);
#endif
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#ifndef __LIMA_SCHED_H__
#define __LIMA_SCHED_H__
#include <drm/gpu_scheduler.h>
struct lima_vm;
struct lima_sched_task {
struct drm_sched_job base;
struct lima_vm *vm;
void *frame;
struct dma_fence **dep;
int num_dep;
int max_dep;
struct lima_bo **bos;
int num_bos;
/* pipe fence */
struct dma_fence *fence;
};
struct lima_sched_context {
struct drm_sched_entity base;
};
#define LIMA_SCHED_PIPE_MAX_MMU 8
#define LIMA_SCHED_PIPE_MAX_L2_CACHE 2
#define LIMA_SCHED_PIPE_MAX_PROCESSOR 8
struct lima_ip;
struct lima_sched_pipe {
struct drm_gpu_scheduler base;
u64 fence_context;
u32 fence_seqno;
spinlock_t fence_lock;
struct lima_sched_task *current_task;
struct lima_vm *current_vm;
struct lima_ip *mmu[LIMA_SCHED_PIPE_MAX_MMU];
int num_mmu;
struct lima_ip *l2_cache[LIMA_SCHED_PIPE_MAX_L2_CACHE];
int num_l2_cache;
struct lima_ip *processor[LIMA_SCHED_PIPE_MAX_PROCESSOR];
int num_processor;
struct lima_ip *bcast_processor;
struct lima_ip *bcast_mmu;
u32 done;
bool error;
atomic_t task;
int frame_size;
struct kmem_cache *task_slab;
int (*task_validate)(struct lima_sched_pipe *pipe, struct lima_sched_task *task);
void (*task_run)(struct lima_sched_pipe *pipe, struct lima_sched_task *task);
void (*task_fini)(struct lima_sched_pipe *pipe);
void (*task_error)(struct lima_sched_pipe *pipe);
void (*task_mmu_error)(struct lima_sched_pipe *pipe);
struct work_struct error_work;
};
int lima_sched_task_init(struct lima_sched_task *task,
struct lima_sched_context *context,
struct lima_bo **bos, int num_bos,
struct lima_vm *vm);
void lima_sched_task_fini(struct lima_sched_task *task);
int lima_sched_task_add_dep(struct lima_sched_task *task, struct dma_fence *fence);
int lima_sched_context_init(struct lima_sched_pipe *pipe,
struct lima_sched_context *context,
atomic_t *guilty);
void lima_sched_context_fini(struct lima_sched_pipe *pipe,
struct lima_sched_context *context);
struct dma_fence *lima_sched_context_queue_task(struct lima_sched_context *context,
struct lima_sched_task *task);
int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name);
void lima_sched_pipe_fini(struct lima_sched_pipe *pipe);
void lima_sched_pipe_task_done(struct lima_sched_pipe *pipe);
static inline void lima_sched_pipe_mmu_error(struct lima_sched_pipe *pipe)
{
pipe->error = true;
pipe->task_mmu_error(pipe);
}
int lima_sched_slab_init(void);
void lima_sched_slab_fini(void);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2,6 +2,7 @@
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
......@@ -13,6 +14,7 @@ obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
obj-$(CONFIG_DRM_PANEL_ROCKTECH_JH057N00900) += panel-rocktech-jh057n00900.o
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment