Commit a8c21a54 authored by The etnaviv authors's avatar The etnaviv authors Committed by Lucas Stach

drm/etnaviv: add initial etnaviv DRM driver

This adds the etnaviv DRM driver and hooks it up in Makefiles
and Kconfig.
Signed-off-by: default avatarChristian Gmeiner <christian.gmeiner@gmail.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarLucas Stach <l.stach@pengutronix.de>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent f04b205a
...@@ -266,3 +266,5 @@ source "drivers/gpu/drm/amd/amdkfd/Kconfig" ...@@ -266,3 +266,5 @@ source "drivers/gpu/drm/amd/amdkfd/Kconfig"
source "drivers/gpu/drm/imx/Kconfig" source "drivers/gpu/drm/imx/Kconfig"
source "drivers/gpu/drm/vc4/Kconfig" source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig"
...@@ -75,3 +75,4 @@ obj-y += i2c/ ...@@ -75,3 +75,4 @@ obj-y += i2c/
obj-y += panel/ obj-y += panel/
obj-y += bridge/ obj-y += bridge/
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
config DRM_ETNAVIV
tristate "ETNAVIV (DRM support for Vivante GPU IP cores)"
depends on DRM
depends on ARCH_MXC || ARCH_DOVE
select SHMEM
select TMPFS
select IOMMU_API
select IOMMU_SUPPORT
select WANT_DEV_COREDUMP
help
DRM driver for Vivante GPUs.
config DRM_ETNAVIV_REGISTER_LOGGING
bool "enable ETNAVIV register logging"
depends on DRM_ETNAVIV
help
Compile in support for logging register reads/writes in a format
that can be parsed by envytools demsm tool. If enabled, register
logging can be switched on via etnaviv.reglog=y module param.
etnaviv-y := \
etnaviv_buffer.o \
etnaviv_cmd_parser.o \
etnaviv_drv.o \
etnaviv_dump.o \
etnaviv_gem_prime.o \
etnaviv_gem_submit.o \
etnaviv_gem.o \
etnaviv_gpu.o \
etnaviv_iommu_v2.o \
etnaviv_iommu.o \
etnaviv_mmu.o
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o
#ifndef CMDSTREAM_XML
#define CMDSTREAM_XML
/* Autogenerated file, DO NOT EDIT manually!
This file was generated by the rules-ng-ng headergen tool in this git repository:
http://0x04.net/cgit/index.cgi/rules-ng-ng
git clone git://0x04.net/rules-ng-ng
The rules-ng-ng source files this header was generated from are:
- cmdstream.xml ( 12589 bytes, from 2014-02-17 14:57:56)
- common.xml ( 18437 bytes, from 2015-03-25 11:27:41)
Copyright (C) 2014
*/
#define FE_OPCODE_LOAD_STATE 0x00000001
#define FE_OPCODE_END 0x00000002
#define FE_OPCODE_NOP 0x00000003
#define FE_OPCODE_DRAW_2D 0x00000004
#define FE_OPCODE_DRAW_PRIMITIVES 0x00000005
#define FE_OPCODE_DRAW_INDEXED_PRIMITIVES 0x00000006
#define FE_OPCODE_WAIT 0x00000007
#define FE_OPCODE_LINK 0x00000008
#define FE_OPCODE_STALL 0x00000009
#define FE_OPCODE_CALL 0x0000000a
#define FE_OPCODE_RETURN 0x0000000b
#define FE_OPCODE_CHIP_SELECT 0x0000000d
#define PRIMITIVE_TYPE_POINTS 0x00000001
#define PRIMITIVE_TYPE_LINES 0x00000002
#define PRIMITIVE_TYPE_LINE_STRIP 0x00000003
#define PRIMITIVE_TYPE_TRIANGLES 0x00000004
#define PRIMITIVE_TYPE_TRIANGLE_STRIP 0x00000005
#define PRIMITIVE_TYPE_TRIANGLE_FAN 0x00000006
#define PRIMITIVE_TYPE_LINE_LOOP 0x00000007
#define PRIMITIVE_TYPE_QUADS 0x00000008
#define VIV_FE_LOAD_STATE 0x00000000
#define VIV_FE_LOAD_STATE_HEADER 0x00000000
#define VIV_FE_LOAD_STATE_HEADER_OP__MASK 0xf8000000
#define VIV_FE_LOAD_STATE_HEADER_OP__SHIFT 27
#define VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE 0x08000000
#define VIV_FE_LOAD_STATE_HEADER_FIXP 0x04000000
#define VIV_FE_LOAD_STATE_HEADER_COUNT__MASK 0x03ff0000
#define VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT 16
#define VIV_FE_LOAD_STATE_HEADER_COUNT(x) (((x) << VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT) & VIV_FE_LOAD_STATE_HEADER_COUNT__MASK)
#define VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK 0x0000ffff
#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT 0
#define VIV_FE_LOAD_STATE_HEADER_OFFSET(x) (((x) << VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT) & VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK)
#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR 2
#define VIV_FE_END 0x00000000
#define VIV_FE_END_HEADER 0x00000000
#define VIV_FE_END_HEADER_EVENT_ID__MASK 0x0000001f
#define VIV_FE_END_HEADER_EVENT_ID__SHIFT 0
#define VIV_FE_END_HEADER_EVENT_ID(x) (((x) << VIV_FE_END_HEADER_EVENT_ID__SHIFT) & VIV_FE_END_HEADER_EVENT_ID__MASK)
#define VIV_FE_END_HEADER_EVENT_ENABLE 0x00000100
#define VIV_FE_END_HEADER_OP__MASK 0xf8000000
#define VIV_FE_END_HEADER_OP__SHIFT 27
#define VIV_FE_END_HEADER_OP_END 0x10000000
#define VIV_FE_NOP 0x00000000
#define VIV_FE_NOP_HEADER 0x00000000
#define VIV_FE_NOP_HEADER_OP__MASK 0xf8000000
#define VIV_FE_NOP_HEADER_OP__SHIFT 27
#define VIV_FE_NOP_HEADER_OP_NOP 0x18000000
#define VIV_FE_DRAW_2D 0x00000000
#define VIV_FE_DRAW_2D_HEADER 0x00000000
#define VIV_FE_DRAW_2D_HEADER_COUNT__MASK 0x0000ff00
#define VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT 8
#define VIV_FE_DRAW_2D_HEADER_COUNT(x) (((x) << VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_COUNT__MASK)
#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK 0x07ff0000
#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT 16
#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT(x) (((x) << VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK)
#define VIV_FE_DRAW_2D_HEADER_OP__MASK 0xf8000000
#define VIV_FE_DRAW_2D_HEADER_OP__SHIFT 27
#define VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D 0x20000000
#define VIV_FE_DRAW_2D_TOP_LEFT 0x00000008
#define VIV_FE_DRAW_2D_TOP_LEFT_X__MASK 0x0000ffff
#define VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT 0
#define VIV_FE_DRAW_2D_TOP_LEFT_X(x) (((x) << VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_X__MASK)
#define VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK 0xffff0000
#define VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT 16
#define VIV_FE_DRAW_2D_TOP_LEFT_Y(x) (((x) << VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK)
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT 0x0000000c
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK 0x0000ffff
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT 0
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(x) (((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK)
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK 0xffff0000
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT 16
#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(x) (((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK)
#define VIV_FE_DRAW_PRIMITIVES 0x00000000
#define VIV_FE_DRAW_PRIMITIVES_HEADER 0x00000000
#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__MASK 0xf8000000
#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__SHIFT 27
#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP_DRAW_PRIMITIVES 0x28000000
#define VIV_FE_DRAW_PRIMITIVES_COMMAND 0x00000004
#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK 0x000000ff
#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT 0
#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE(x) (((x) << VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK)
#define VIV_FE_DRAW_PRIMITIVES_START 0x00000008
#define VIV_FE_DRAW_PRIMITIVES_COUNT 0x0000000c
#define VIV_FE_DRAW_INDEXED_PRIMITIVES 0x00000000
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER 0x00000000
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__MASK 0xf8000000
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__SHIFT 27
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP_DRAW_INDEXED_PRIMITIVES 0x30000000
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND 0x00000004
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK 0x000000ff
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT 0
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE(x) (((x) << VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK)
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_START 0x00000008
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COUNT 0x0000000c
#define VIV_FE_DRAW_INDEXED_PRIMITIVES_OFFSET 0x00000010
#define VIV_FE_WAIT 0x00000000
#define VIV_FE_WAIT_HEADER 0x00000000
#define VIV_FE_WAIT_HEADER_DELAY__MASK 0x0000ffff
#define VIV_FE_WAIT_HEADER_DELAY__SHIFT 0
#define VIV_FE_WAIT_HEADER_DELAY(x) (((x) << VIV_FE_WAIT_HEADER_DELAY__SHIFT) & VIV_FE_WAIT_HEADER_DELAY__MASK)
#define VIV_FE_WAIT_HEADER_OP__MASK 0xf8000000
#define VIV_FE_WAIT_HEADER_OP__SHIFT 27
#define VIV_FE_WAIT_HEADER_OP_WAIT 0x38000000
#define VIV_FE_LINK 0x00000000
#define VIV_FE_LINK_HEADER 0x00000000
#define VIV_FE_LINK_HEADER_PREFETCH__MASK 0x0000ffff
#define VIV_FE_LINK_HEADER_PREFETCH__SHIFT 0
#define VIV_FE_LINK_HEADER_PREFETCH(x) (((x) << VIV_FE_LINK_HEADER_PREFETCH__SHIFT) & VIV_FE_LINK_HEADER_PREFETCH__MASK)
#define VIV_FE_LINK_HEADER_OP__MASK 0xf8000000
#define VIV_FE_LINK_HEADER_OP__SHIFT 27
#define VIV_FE_LINK_HEADER_OP_LINK 0x40000000
#define VIV_FE_LINK_ADDRESS 0x00000004
#define VIV_FE_STALL 0x00000000
#define VIV_FE_STALL_HEADER 0x00000000
#define VIV_FE_STALL_HEADER_OP__MASK 0xf8000000
#define VIV_FE_STALL_HEADER_OP__SHIFT 27
#define VIV_FE_STALL_HEADER_OP_STALL 0x48000000
#define VIV_FE_STALL_TOKEN 0x00000004
#define VIV_FE_STALL_TOKEN_FROM__MASK 0x0000001f
#define VIV_FE_STALL_TOKEN_FROM__SHIFT 0
#define VIV_FE_STALL_TOKEN_FROM(x) (((x) << VIV_FE_STALL_TOKEN_FROM__SHIFT) & VIV_FE_STALL_TOKEN_FROM__MASK)
#define VIV_FE_STALL_TOKEN_TO__MASK 0x00001f00
#define VIV_FE_STALL_TOKEN_TO__SHIFT 8
#define VIV_FE_STALL_TOKEN_TO(x) (((x) << VIV_FE_STALL_TOKEN_TO__SHIFT) & VIV_FE_STALL_TOKEN_TO__MASK)
#define VIV_FE_CALL 0x00000000
#define VIV_FE_CALL_HEADER 0x00000000
#define VIV_FE_CALL_HEADER_PREFETCH__MASK 0x0000ffff
#define VIV_FE_CALL_HEADER_PREFETCH__SHIFT 0
#define VIV_FE_CALL_HEADER_PREFETCH(x) (((x) << VIV_FE_CALL_HEADER_PREFETCH__SHIFT) & VIV_FE_CALL_HEADER_PREFETCH__MASK)
#define VIV_FE_CALL_HEADER_OP__MASK 0xf8000000
#define VIV_FE_CALL_HEADER_OP__SHIFT 27
#define VIV_FE_CALL_HEADER_OP_CALL 0x50000000
#define VIV_FE_CALL_ADDRESS 0x00000004
#define VIV_FE_CALL_RETURN_PREFETCH 0x00000008
#define VIV_FE_CALL_RETURN_ADDRESS 0x0000000c
#define VIV_FE_RETURN 0x00000000
#define VIV_FE_RETURN_HEADER 0x00000000
#define VIV_FE_RETURN_HEADER_OP__MASK 0xf8000000
#define VIV_FE_RETURN_HEADER_OP__SHIFT 27
#define VIV_FE_RETURN_HEADER_OP_RETURN 0x58000000
#define VIV_FE_CHIP_SELECT 0x00000000
#define VIV_FE_CHIP_SELECT_HEADER 0x00000000
#define VIV_FE_CHIP_SELECT_HEADER_OP__MASK 0xf8000000
#define VIV_FE_CHIP_SELECT_HEADER_OP__SHIFT 27
#define VIV_FE_CHIP_SELECT_HEADER_OP_CHIP_SELECT 0x68000000
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP15 0x00008000
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP14 0x00004000
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP13 0x00002000
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP12 0x00001000
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP11 0x00000800
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP10 0x00000400
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP9 0x00000200
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP8 0x00000100
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP7 0x00000080
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP6 0x00000040
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP5 0x00000020
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP4 0x00000010
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP3 0x00000008
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP2 0x00000004
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP1 0x00000002
#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP0 0x00000001
#endif /* CMDSTREAM_XML */
This diff is collapsed.
/*
* Copyright (C) 2014 Etnaviv Project
* Author: Christian Gmeiner <christian.gmeiner@gmail.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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "etnaviv_gpu.h"
#include "etnaviv_gem.h"
#include "etnaviv_mmu.h"
#include "common.xml.h"
#include "state.xml.h"
#include "cmdstream.xml.h"
/*
* Command Buffer helper:
*/
static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data)
{
u32 *vaddr = (u32 *)buffer->vaddr;
BUG_ON(buffer->user_size >= buffer->size);
vaddr[buffer->user_size / 4] = data;
buffer->user_size += 4;
}
static inline void CMD_LOAD_STATE(struct etnaviv_cmdbuf *buffer,
u32 reg, u32 value)
{
u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR;
buffer->user_size = ALIGN(buffer->user_size, 8);
/* write a register via cmd stream */
OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE |
VIV_FE_LOAD_STATE_HEADER_COUNT(1) |
VIV_FE_LOAD_STATE_HEADER_OFFSET(index));
OUT(buffer, value);
}
static inline void CMD_END(struct etnaviv_cmdbuf *buffer)
{
buffer->user_size = ALIGN(buffer->user_size, 8);
OUT(buffer, VIV_FE_END_HEADER_OP_END);
}
static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer)
{
buffer->user_size = ALIGN(buffer->user_size, 8);
OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | 200);
}
static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer,
u16 prefetch, u32 address)
{
buffer->user_size = ALIGN(buffer->user_size, 8);
OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK |
VIV_FE_LINK_HEADER_PREFETCH(prefetch));
OUT(buffer, address);
}
static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer,
u32 from, u32 to)
{
buffer->user_size = ALIGN(buffer->user_size, 8);
OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL);
OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to));
}
static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe)
{
u32 flush;
u32 stall;
/*
* This assumes that if we're switching to 2D, we're switching
* away from 3D, and vice versa. Hence, if we're switching to
* the 2D core, we need to flush the 3D depth and color caches,
* otherwise we need to flush the 2D pixel engine cache.
*/
if (pipe == ETNA_PIPE_2D)
flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR;
else
flush = VIVS_GL_FLUSH_CACHE_PE2D;
stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) |
VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE);
CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush);
CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall);
CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
VIVS_GL_PIPE_SELECT_PIPE(pipe));
}
static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf)
{
return buf->paddr - gpu->memory_base;
}
static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
struct etnaviv_cmdbuf *buf, u32 off, u32 len)
{
u32 size = buf->size;
u32 *ptr = buf->vaddr + off;
dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n",
ptr, gpu_va(gpu, buf) + off, size - len * 4 - off);
print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
ptr, len * 4, 0);
}
u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
{
struct etnaviv_cmdbuf *buffer = gpu->buffer;
/* initialize buffer */
buffer->user_size = 0;
CMD_WAIT(buffer);
CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4);
return buffer->user_size / 8;
}
void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
{
struct etnaviv_cmdbuf *buffer = gpu->buffer;
/* Replace the last WAIT with an END */
buffer->user_size -= 16;
CMD_END(buffer);
mb();
}
void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
struct etnaviv_cmdbuf *cmdbuf)
{
struct etnaviv_cmdbuf *buffer = gpu->buffer;
u32 *lw = buffer->vaddr + buffer->user_size - 16;
u32 back, link_target, link_size, reserve_size, extra_size = 0;
if (drm_debug & DRM_UT_DRIVER)
etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
/*
* If we need to flush the MMU prior to submitting this buffer, we
* will need to append a mmu flush load state, followed by a new
* link to this buffer - a total of four additional words.
*/
if (gpu->mmu->need_flush || gpu->switch_context) {
/* link command */
extra_size += 2;
/* flush command */
if (gpu->mmu->need_flush)
extra_size += 2;
/* pipe switch commands */
if (gpu->switch_context)
extra_size += 8;
}
reserve_size = (6 + extra_size) * 4;
/*
* if we are going to completely overflow the buffer, we need to wrap.
*/
if (buffer->user_size + reserve_size > buffer->size)
buffer->user_size = 0;
/* save offset back into main buffer */
back = buffer->user_size + reserve_size - 6 * 4;
link_target = gpu_va(gpu, buffer) + buffer->user_size;
link_size = 6;
/* Skip over any extra instructions */
link_target += extra_size * sizeof(u32);
if (drm_debug & DRM_UT_DRIVER)
pr_info("stream link to 0x%08x @ 0x%08x %p\n",
link_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr);
/* jump back from cmd to main buffer */
CMD_LINK(cmdbuf, link_size, link_target);
link_target = gpu_va(gpu, cmdbuf);
link_size = cmdbuf->size / 8;
if (drm_debug & DRM_UT_DRIVER) {
print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
cmdbuf->vaddr, cmdbuf->size, 0);
pr_info("link op: %p\n", lw);
pr_info("link addr: %p\n", lw + 1);
pr_info("addr: 0x%08x\n", link_target);
pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + back);
pr_info("event: %d\n", event);
}
if (gpu->mmu->need_flush || gpu->switch_context) {
u32 new_target = gpu_va(gpu, buffer) + buffer->user_size;
if (gpu->mmu->need_flush) {
/* Add the MMU flush */
CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
gpu->mmu->need_flush = false;
}
if (gpu->switch_context) {
etnaviv_cmd_select_pipe(buffer, cmdbuf->exec_state);
gpu->switch_context = false;
}
/* And the link to the first buffer */
CMD_LINK(buffer, link_size, link_target);
/* Update the link target to point to above instructions */
link_target = new_target;
link_size = extra_size;
}
/* trigger event */
CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
VIVS_GL_EVENT_FROM_PE);
/* append WAIT/LINK to main buffer */
CMD_WAIT(buffer);
CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + (buffer->user_size - 4));
/* Change WAIT into a LINK command; write the address first. */
*(lw + 1) = link_target;
mb();
*(lw) = VIV_FE_LINK_HEADER_OP_LINK |
VIV_FE_LINK_HEADER_PREFETCH(link_size);
mb();
if (drm_debug & DRM_UT_DRIVER)
etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
}
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include "etnaviv_gem.h"
#include "etnaviv_gpu.h"
#include "cmdstream.xml.h"
#define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT)
struct etna_validation_state {
struct etnaviv_gpu *gpu;
const struct drm_etnaviv_gem_submit_reloc *relocs;
unsigned int num_relocs;
u32 *start;
};
static const struct {
u16 offset;
u16 size;
} etnaviv_sensitive_states[] __initconst = {
#define ST(start, num) { (start) >> 2, (num) }
/* 2D */
ST(0x1200, 1),
ST(0x1228, 1),
ST(0x1238, 1),
ST(0x1284, 1),
ST(0x128c, 1),
ST(0x1304, 1),
ST(0x1310, 1),
ST(0x1318, 1),
ST(0x12800, 4),
ST(0x128a0, 4),
ST(0x128c0, 4),
ST(0x12970, 4),
ST(0x12a00, 8),
ST(0x12b40, 8),
ST(0x12b80, 8),
ST(0x12ce0, 8),
/* 3D */
ST(0x0644, 1),
ST(0x064c, 1),
ST(0x0680, 8),
ST(0x1410, 1),
ST(0x1430, 1),
ST(0x1458, 1),
ST(0x1460, 8),
ST(0x1480, 8),
ST(0x1500, 8),
ST(0x1520, 8),
ST(0x1608, 1),
ST(0x1610, 1),
ST(0x1658, 1),
ST(0x165c, 1),
ST(0x1664, 1),
ST(0x1668, 1),
ST(0x16a4, 1),
ST(0x16c0, 8),
ST(0x16e0, 8),
ST(0x1740, 8),
ST(0x2400, 14 * 16),
ST(0x10800, 32 * 16),
#undef ST
};
#define ETNAVIV_STATES_SIZE (VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK + 1u)
static DECLARE_BITMAP(etnaviv_states, ETNAVIV_STATES_SIZE);
void __init etnaviv_validate_init(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(etnaviv_sensitive_states); i++)
bitmap_set(etnaviv_states, etnaviv_sensitive_states[i].offset,
etnaviv_sensitive_states[i].size);
}
static void etnaviv_warn_if_non_sensitive(struct etna_validation_state *state,
unsigned int buf_offset, unsigned int state_addr)
{
if (state->num_relocs && state->relocs->submit_offset < buf_offset) {
dev_warn_once(state->gpu->dev,
"%s: relocation for non-sensitive state 0x%x at offset %u\n",
__func__, state_addr,
state->relocs->submit_offset);
while (state->num_relocs &&
state->relocs->submit_offset < buf_offset) {
state->relocs++;
state->num_relocs--;
}
}
}
static bool etnaviv_validate_load_state(struct etna_validation_state *state,
u32 *ptr, unsigned int state_offset, unsigned int num)
{
unsigned int size = min(ETNAVIV_STATES_SIZE, state_offset + num);
unsigned int st_offset = state_offset, buf_offset;
for_each_set_bit_from(st_offset, etnaviv_states, size) {
buf_offset = (ptr - state->start +
st_offset - state_offset) * 4;
etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4);
if (state->num_relocs &&
state->relocs->submit_offset == buf_offset) {
state->relocs++;
state->num_relocs--;
continue;
}
dev_warn_ratelimited(state->gpu->dev,
"%s: load state touches restricted state 0x%x at offset %u\n",
__func__, st_offset * 4, buf_offset);
return false;
}
if (state->num_relocs) {
buf_offset = (ptr - state->start + num) * 4;
etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4 +
state->relocs->submit_offset -
buf_offset);
}
return true;
}
static uint8_t cmd_length[32] = {
[FE_OPCODE_DRAW_PRIMITIVES] = 4,
[FE_OPCODE_DRAW_INDEXED_PRIMITIVES] = 6,
[FE_OPCODE_NOP] = 2,
[FE_OPCODE_STALL] = 2,
};
bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, u32 *stream,
unsigned int size,
struct drm_etnaviv_gem_submit_reloc *relocs,
unsigned int reloc_size)
{
struct etna_validation_state state;
u32 *buf = stream;
u32 *end = buf + size;
state.gpu = gpu;
state.relocs = relocs;
state.num_relocs = reloc_size;
state.start = stream;
while (buf < end) {
u32 cmd = *buf;
unsigned int len, n, off;
unsigned int op = cmd >> 27;
switch (op) {
case FE_OPCODE_LOAD_STATE:
n = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_COUNT);
len = ALIGN(1 + n, 2);
if (buf + len > end)
break;
off = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_OFFSET);
if (!etnaviv_validate_load_state(&state, buf + 1,
off, n))
return false;
break;
case FE_OPCODE_DRAW_2D:
n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT);
if (n == 0)
n = 256;
len = 2 + n * 2;
break;
default:
len = cmd_length[op];
if (len == 0) {
dev_err(gpu->dev, "%s: op %u not permitted at offset %tu\n",
__func__, op, buf - state.start);
return false;
}
break;
}
buf += len;
}
if (buf > end) {
dev_err(gpu->dev, "%s: commands overflow end of buffer: %tu > %u\n",
__func__, buf - state.start, size);
return false;
}
return true;
}
This diff is collapsed.
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETNAVIV_DRV_H__
#define __ETNAVIV_DRV_H__
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/sizes.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem.h>
#include <drm/etnaviv_drm.h>
struct etnaviv_cmdbuf;
struct etnaviv_gpu;
struct etnaviv_mmu;
struct etnaviv_gem_object;
struct etnaviv_gem_submit;
struct etnaviv_file_private {
/* currently we don't do anything useful with this.. but when
* per-context address spaces are supported we'd keep track of
* the context's page-tables here.
*/
int dummy;
};
struct etnaviv_drm_private {
int num_gpus;
struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
/* list of GEM objects: */
struct mutex gem_lock;
struct list_head gem_list;
struct workqueue_struct *wq;
};
static inline void etnaviv_queue_work(struct drm_device *dev,
struct work_struct *w)
{
struct etnaviv_drm_private *priv = dev->dev_private;
queue_work(priv->wq, w);
}
int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
struct drm_file *file);
int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma);
int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset);
int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu,
struct drm_gem_object *obj, u32 *iova);
void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj);
struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj);
void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct sg_table *sg);
int etnaviv_gem_prime_pin(struct drm_gem_object *obj);
void etnaviv_gem_prime_unpin(struct drm_gem_object *obj);
void *etnaviv_gem_vaddr(struct drm_gem_object *obj);
int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
struct timespec *timeout);
int etnaviv_gem_cpu_fini(struct drm_gem_object *obj);
void etnaviv_gem_free_object(struct drm_gem_object *obj);
int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
u32 size, u32 flags, u32 *handle);
struct drm_gem_object *etnaviv_gem_new_locked(struct drm_device *dev,
u32 size, u32 flags);
struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
u32 size, u32 flags);
int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
uintptr_t ptr, u32 size, u32 flags, u32 *handle);
u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
struct etnaviv_cmdbuf *cmdbuf);
void etnaviv_validate_init(void);
bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
u32 *stream, unsigned int size,
struct drm_etnaviv_gem_submit_reloc *relocs, unsigned int reloc_size);
#ifdef CONFIG_DEBUG_FS
void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv,
struct seq_file *m);
#endif
void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name,
const char *dbgname);
void etnaviv_writel(u32 data, void __iomem *addr);
u32 etnaviv_readl(const void __iomem *addr);
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
/*
* Return the storage size of a structure with a variable length array.
* The array is nelem elements of elem_size, where the base structure
* is defined by base. If the size overflows size_t, return zero.
*/
static inline size_t size_vstruct(size_t nelem, size_t elem_size, size_t base)
{
if (elem_size && nelem > (SIZE_MAX - base) / elem_size)
return 0;
return base + nelem * elem_size;
}
/* returns true if fence a comes after fence b */
static inline bool fence_after(u32 a, u32 b)
{
return (s32)(a - b) > 0;
}
static inline bool fence_after_eq(u32 a, u32 b)
{
return (s32)(a - b) >= 0;
}
static inline unsigned long etnaviv_timeout_to_jiffies(
const struct timespec *timeout)
{
unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
unsigned long start_jiffies = jiffies;
unsigned long remaining_jiffies;
if (time_after(start_jiffies, timeout_jiffies))
remaining_jiffies = 0;
else
remaining_jiffies = timeout_jiffies - start_jiffies;
return remaining_jiffies;
}
#endif /* __ETNAVIV_DRV_H__ */
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/devcoredump.h>
#include "etnaviv_dump.h"
#include "etnaviv_gem.h"
#include "etnaviv_gpu.h"
#include "etnaviv_mmu.h"
#include "state.xml.h"
#include "state_hi.xml.h"
struct core_dump_iterator {
void *start;
struct etnaviv_dump_object_header *hdr;
void *data;
};
static const unsigned short etnaviv_dump_registers[] = {
VIVS_HI_AXI_STATUS,
VIVS_HI_CLOCK_CONTROL,
VIVS_HI_IDLE_STATE,
VIVS_HI_AXI_CONFIG,
VIVS_HI_INTR_ENBL,
VIVS_HI_CHIP_IDENTITY,
VIVS_HI_CHIP_FEATURE,
VIVS_HI_CHIP_MODEL,
VIVS_HI_CHIP_REV,
VIVS_HI_CHIP_DATE,
VIVS_HI_CHIP_TIME,
VIVS_HI_CHIP_MINOR_FEATURE_0,
VIVS_HI_CACHE_CONTROL,
VIVS_HI_AXI_CONTROL,
VIVS_PM_POWER_CONTROLS,
VIVS_PM_MODULE_CONTROLS,
VIVS_PM_MODULE_STATUS,
VIVS_PM_PULSE_EATER,
VIVS_MC_MMU_FE_PAGE_TABLE,
VIVS_MC_MMU_TX_PAGE_TABLE,
VIVS_MC_MMU_PE_PAGE_TABLE,
VIVS_MC_MMU_PEZ_PAGE_TABLE,
VIVS_MC_MMU_RA_PAGE_TABLE,
VIVS_MC_DEBUG_MEMORY,
VIVS_MC_MEMORY_BASE_ADDR_RA,
VIVS_MC_MEMORY_BASE_ADDR_FE,
VIVS_MC_MEMORY_BASE_ADDR_TX,
VIVS_MC_MEMORY_BASE_ADDR_PEZ,
VIVS_MC_MEMORY_BASE_ADDR_PE,
VIVS_MC_MEMORY_TIMING_CONTROL,
VIVS_MC_BUS_CONFIG,
VIVS_FE_DMA_STATUS,
VIVS_FE_DMA_DEBUG_STATE,
VIVS_FE_DMA_ADDRESS,
VIVS_FE_DMA_LOW,
VIVS_FE_DMA_HIGH,
VIVS_FE_AUTO_FLUSH,
};
static void etnaviv_core_dump_header(struct core_dump_iterator *iter,
u32 type, void *data_end)
{
struct etnaviv_dump_object_header *hdr = iter->hdr;
hdr->magic = cpu_to_le32(ETDUMP_MAGIC);
hdr->type = cpu_to_le32(type);
hdr->file_offset = cpu_to_le32(iter->data - iter->start);
hdr->file_size = cpu_to_le32(data_end - iter->data);
iter->hdr++;
iter->data += hdr->file_size;
}
static void etnaviv_core_dump_registers(struct core_dump_iterator *iter,
struct etnaviv_gpu *gpu)
{
struct etnaviv_dump_registers *reg = iter->data;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(etnaviv_dump_registers); i++, reg++) {
reg->reg = etnaviv_dump_registers[i];
reg->value = gpu_read(gpu, etnaviv_dump_registers[i]);
}
etnaviv_core_dump_header(iter, ETDUMP_BUF_REG, reg);
}
static void etnaviv_core_dump_mmu(struct core_dump_iterator *iter,
struct etnaviv_gpu *gpu, size_t mmu_size)
{
etnaviv_iommu_dump(gpu->mmu, iter->data);
etnaviv_core_dump_header(iter, ETDUMP_BUF_MMU, iter->data + mmu_size);
}
static void etnaviv_core_dump_mem(struct core_dump_iterator *iter, u32 type,
void *ptr, size_t size, u64 iova)
{
memcpy(iter->data, ptr, size);
iter->hdr->iova = cpu_to_le64(iova);
etnaviv_core_dump_header(iter, type, iter->data + size);
}
void etnaviv_core_dump(struct etnaviv_gpu *gpu)
{
struct core_dump_iterator iter;
struct etnaviv_vram_mapping *vram;
struct etnaviv_gem_object *obj;
struct etnaviv_cmdbuf *cmd;
unsigned int n_obj, n_bomap_pages;
size_t file_size, mmu_size;
__le64 *bomap, *bomap_start;
mmu_size = etnaviv_iommu_dump_size(gpu->mmu);
/* We always dump registers, mmu, ring and end marker */
n_obj = 4;
n_bomap_pages = 0;
file_size = ARRAY_SIZE(etnaviv_dump_registers) *
sizeof(struct etnaviv_dump_registers) +
mmu_size + gpu->buffer->size;
/* Add in the active command buffers */
list_for_each_entry(cmd, &gpu->active_cmd_list, node) {
file_size += cmd->size;
n_obj++;
}
/* Add in the active buffer objects */
list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) {
if (!vram->use)
continue;
obj = vram->object;
file_size += obj->base.size;
n_bomap_pages += obj->base.size >> PAGE_SHIFT;
n_obj++;
}
/* If we have any buffer objects, add a bomap object */
if (n_bomap_pages) {
file_size += n_bomap_pages * sizeof(__le64);
n_obj++;
}
/* Add the size of the headers */
file_size += sizeof(*iter.hdr) * n_obj;
/* Allocate the file in vmalloc memory, it's likely to be big */
iter.start = vmalloc(file_size);
if (!iter.start) {
dev_warn(gpu->dev, "failed to allocate devcoredump file\n");
return;
}
/* Point the data member after the headers */
iter.hdr = iter.start;
iter.data = &iter.hdr[n_obj];
memset(iter.hdr, 0, iter.data - iter.start);
etnaviv_core_dump_registers(&iter, gpu);
etnaviv_core_dump_mmu(&iter, gpu, mmu_size);
etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr,
gpu->buffer->size, gpu->buffer->paddr);
list_for_each_entry(cmd, &gpu->active_cmd_list, node)
etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr,
cmd->size, cmd->paddr);
/* Reserve space for the bomap */
if (n_bomap_pages) {
bomap_start = bomap = iter.data;
memset(bomap, 0, sizeof(*bomap) * n_bomap_pages);
etnaviv_core_dump_header(&iter, ETDUMP_BUF_BOMAP,
bomap + n_bomap_pages);
} else {
/* Silence warning */
bomap_start = bomap = NULL;
}
list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) {
struct page **pages;
void *vaddr;
if (vram->use == 0)
continue;
obj = vram->object;
pages = etnaviv_gem_get_pages(obj);
if (pages) {
int j;
iter.hdr->data[0] = bomap - bomap_start;
for (j = 0; j < obj->base.size >> PAGE_SHIFT; j++)
*bomap++ = cpu_to_le64(page_to_phys(*pages++));
}
iter.hdr->iova = cpu_to_le64(vram->iova);
vaddr = etnaviv_gem_vaddr(&obj->base);
if (vaddr && !IS_ERR(vaddr))
memcpy(iter.data, vaddr, obj->base.size);
etnaviv_core_dump_header(&iter, ETDUMP_BUF_BO, iter.data +
obj->base.size);
}
etnaviv_core_dump_header(&iter, ETDUMP_BUF_END, iter.data);
dev_coredumpv(gpu->dev, iter.start, iter.data - iter.start, GFP_KERNEL);
}
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* Etnaviv devcoredump file definitions
*/
#ifndef ETNAVIV_DUMP_H
#define ETNAVIV_DUMP_H
#include <linux/types.h>
enum {
ETDUMP_MAGIC = 0x414e5445,
ETDUMP_BUF_REG = 0,
ETDUMP_BUF_MMU,
ETDUMP_BUF_RING,
ETDUMP_BUF_CMD,
ETDUMP_BUF_BOMAP,
ETDUMP_BUF_BO,
ETDUMP_BUF_END,
};
struct etnaviv_dump_object_header {
__le32 magic;
__le32 type;
__le32 file_offset;
__le32 file_size;
__le64 iova;
__le32 data[2];
};
/* Registers object, an array of these */
struct etnaviv_dump_registers {
__le32 reg;
__le32 value;
};
#ifdef __KERNEL__
struct etnaviv_gpu;
void etnaviv_core_dump(struct etnaviv_gpu *gpu);
#endif
#endif
This diff is collapsed.
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETNAVIV_GEM_H__
#define __ETNAVIV_GEM_H__
#include <linux/reservation.h>
#include "etnaviv_drv.h"
struct etnaviv_gem_ops;
struct etnaviv_gem_object;
struct etnaviv_gem_userptr {
uintptr_t ptr;
struct task_struct *task;
struct work_struct *work;
bool ro;
};
struct etnaviv_vram_mapping {
struct list_head obj_node;
struct list_head scan_node;
struct list_head mmu_node;
struct etnaviv_gem_object *object;
struct etnaviv_iommu *mmu;
struct drm_mm_node vram_node;
unsigned int use;
u32 iova;
};
struct etnaviv_gem_object {
struct drm_gem_object base;
const struct etnaviv_gem_ops *ops;
struct mutex lock;
u32 flags;
struct list_head gem_node;
struct etnaviv_gpu *gpu; /* non-null if active */
atomic_t gpu_active;
u32 access;
struct page **pages;
struct sg_table *sgt;
void *vaddr;
/* normally (resv == &_resv) except for imported bo's */
struct reservation_object *resv;
struct reservation_object _resv;
struct list_head vram_list;
/* cache maintenance */
u32 last_cpu_prep_op;
struct etnaviv_gem_userptr userptr;
};
static inline
struct etnaviv_gem_object *to_etnaviv_bo(struct drm_gem_object *obj)
{
return container_of(obj, struct etnaviv_gem_object, base);
}
struct etnaviv_gem_ops {
int (*get_pages)(struct etnaviv_gem_object *);
void (*release)(struct etnaviv_gem_object *);
};
static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
{
return atomic_read(&etnaviv_obj->gpu_active) != 0;
}
#define MAX_CMDS 4
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
* associated with the cmdstream submission for synchronization (and
* make it easier to unwind when things go wrong, etc). This only
* lasts for the duration of the submit-ioctl.
*/
struct etnaviv_gem_submit {
struct drm_device *dev;
struct etnaviv_gpu *gpu;
struct ww_acquire_ctx ticket;
u32 fence;
unsigned int nr_bos;
struct {
u32 flags;
struct etnaviv_gem_object *obj;
u32 iova;
} bos[0];
};
int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
struct timespec *timeout);
int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
struct reservation_object *robj, const struct etnaviv_gem_ops *ops,
struct etnaviv_gem_object **res);
int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj);
struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj);
void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj);
#endif /* __ETNAVIV_GEM_H__ */
/*
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/dma-buf.h>
#include "etnaviv_drv.h"
#include "etnaviv_gem.h"
struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
BUG_ON(!etnaviv_obj->sgt); /* should have already pinned! */
return etnaviv_obj->sgt;
}
void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj)
{
return etnaviv_gem_vaddr(obj);
}
void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
{
/* TODO msm_gem_vunmap() */
}
int etnaviv_gem_prime_pin(struct drm_gem_object *obj)
{
if (!obj->import_attach) {
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
mutex_lock(&etnaviv_obj->lock);
etnaviv_gem_get_pages(etnaviv_obj);
mutex_unlock(&etnaviv_obj->lock);
}
return 0;
}
void etnaviv_gem_prime_unpin(struct drm_gem_object *obj)
{
if (!obj->import_attach) {
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
mutex_lock(&etnaviv_obj->lock);
etnaviv_gem_put_pages(to_etnaviv_bo(obj));
mutex_unlock(&etnaviv_obj->lock);
}
}
static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj)
{
if (etnaviv_obj->vaddr)
dma_buf_vunmap(etnaviv_obj->base.import_attach->dmabuf,
etnaviv_obj->vaddr);
/* Don't drop the pages for imported dmabuf, as they are not
* ours, just free the array we allocated:
*/
if (etnaviv_obj->pages)
drm_free_large(etnaviv_obj->pages);
drm_prime_gem_destroy(&etnaviv_obj->base, etnaviv_obj->sgt);
}
static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = {
/* .get_pages should never be called */
.release = etnaviv_gem_prime_release,
};
struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct sg_table *sgt)
{
struct etnaviv_gem_object *etnaviv_obj;
size_t size = PAGE_ALIGN(attach->dmabuf->size);
int ret, npages;
ret = etnaviv_gem_new_private(dev, size, ETNA_BO_WC,
attach->dmabuf->resv,
&etnaviv_gem_prime_ops, &etnaviv_obj);
if (ret < 0)
return ERR_PTR(ret);
npages = size / PAGE_SIZE;
etnaviv_obj->sgt = sgt;
etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
if (!etnaviv_obj->pages) {
ret = -ENOMEM;
goto fail;
}
ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages,
NULL, npages);
if (ret)
goto fail;
ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base);
if (ret)
goto fail;
return &etnaviv_obj->base;
fail:
drm_gem_object_unreference_unlocked(&etnaviv_obj->base);
return ERR_PTR(ret);
}
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETNAVIV_GPU_H__
#define __ETNAVIV_GPU_H__
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include "etnaviv_drv.h"
struct etnaviv_gem_submit;
struct etnaviv_chip_identity {
/* Chip model. */
u32 model;
/* Revision value.*/
u32 revision;
/* Supported feature fields. */
u32 features;
/* Supported minor feature fields. */
u32 minor_features0;
/* Supported minor feature 1 fields. */
u32 minor_features1;
/* Supported minor feature 2 fields. */
u32 minor_features2;
/* Supported minor feature 3 fields. */
u32 minor_features3;
/* Number of streams supported. */
u32 stream_count;
/* Total number of temporary registers per thread. */
u32 register_max;
/* Maximum number of threads. */
u32 thread_count;
/* Number of shader cores. */
u32 shader_core_count;
/* Size of the vertex cache. */
u32 vertex_cache_size;
/* Number of entries in the vertex output buffer. */
u32 vertex_output_buffer_size;
/* Number of pixel pipes. */
u32 pixel_pipes;
/* Number of instructions. */
u32 instruction_count;
/* Number of constants. */
u32 num_constants;
/* Buffer size */
u32 buffer_size;
};
struct etnaviv_event {
bool used;
struct fence *fence;
};
struct etnaviv_cmdbuf;
struct etnaviv_gpu {
struct drm_device *drm;
struct device *dev;
struct mutex lock;
struct etnaviv_chip_identity identity;
struct etnaviv_file_private *lastctx;
bool switch_context;
/* 'ring'-buffer: */
struct etnaviv_cmdbuf *buffer;
/* bus base address of memory */
u32 memory_base;
/* event management: */
struct etnaviv_event event[30];
struct completion event_free;
spinlock_t event_spinlock;
/* list of currently in-flight command buffers */
struct list_head active_cmd_list;
u32 idle_mask;
/* Fencing support */
u32 next_fence;
u32 active_fence;
u32 completed_fence;
u32 retired_fence;
wait_queue_head_t fence_event;
unsigned int fence_context;
spinlock_t fence_spinlock;
/* worker for handling active-list retiring: */
struct work_struct retire_work;
void __iomem *mmio;
int irq;
struct etnaviv_iommu *mmu;
/* Power Control: */
struct clk *clk_bus;
struct clk *clk_core;
struct clk *clk_shader;
/* Hang Detction: */
#define DRM_ETNAVIV_HANGCHECK_PERIOD 500 /* in ms */
#define DRM_ETNAVIV_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_ETNAVIV_HANGCHECK_PERIOD)
struct timer_list hangcheck_timer;
u32 hangcheck_fence;
u32 hangcheck_dma_addr;
struct work_struct recover_work;
};
struct etnaviv_cmdbuf {
/* device this cmdbuf is allocated for */
struct etnaviv_gpu *gpu;
/* user context key, must be unique between all active users */
struct etnaviv_file_private *ctx;
/* cmdbuf properties */
void *vaddr;
dma_addr_t paddr;
u32 size;
u32 user_size;
/* fence after which this buffer is to be disposed */
struct fence *fence;
/* target exec state */
u32 exec_state;
/* per GPU in-flight list */
struct list_head node;
/* BOs attached to this command buffer */
unsigned int nr_bos;
struct etnaviv_gem_object *bo[0];
};
static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data)
{
etnaviv_writel(data, gpu->mmio + reg);
}
static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg)
{
return etnaviv_readl(gpu->mmio + reg);
}
static inline bool fence_completed(struct etnaviv_gpu *gpu, u32 fence)
{
return fence_after_eq(gpu->completed_fence, fence);
}
static inline bool fence_retired(struct etnaviv_gpu *gpu, u32 fence)
{
return fence_after_eq(gpu->retired_fence, fence);
}
int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value);
int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
#ifdef CONFIG_DEBUG_FS
int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
#endif
int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj,
unsigned int context, bool exclusive);
void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
u32 fence, struct timespec *timeout);
int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu,
struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout);
int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf);
struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu,
u32 size, size_t nr_bos);
void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf);
int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu);
extern struct platform_driver etnaviv_gpu_driver;
#endif /* __ETNAVIV_GPU_H__ */
/*
* Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include "etnaviv_gpu.h"
#include "etnaviv_mmu.h"
#include "etnaviv_iommu.h"
#include "state_hi.xml.h"
#define PT_SIZE SZ_2M
#define PT_ENTRIES (PT_SIZE / sizeof(u32))
#define GPU_MEM_START 0x80000000
struct etnaviv_iommu_domain_pgtable {
u32 *pgtable;
dma_addr_t paddr;
};
struct etnaviv_iommu_domain {
struct iommu_domain domain;
struct device *dev;
void *bad_page_cpu;
dma_addr_t bad_page_dma;
struct etnaviv_iommu_domain_pgtable pgtable;
spinlock_t map_lock;
};
static struct etnaviv_iommu_domain *to_etnaviv_domain(struct iommu_domain *domain)
{
return container_of(domain, struct etnaviv_iommu_domain, domain);
}
static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable,
size_t size)
{
pgtable->pgtable = dma_alloc_coherent(NULL, size, &pgtable->paddr, GFP_KERNEL);
if (!pgtable->pgtable)
return -ENOMEM;
return 0;
}
static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable,
size_t size)
{
dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr);
}
static u32 pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable,
unsigned long iova)
{
/* calcuate index into page table */
unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
phys_addr_t paddr;
paddr = pgtable->pgtable[index];
return paddr;
}
static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
unsigned long iova, phys_addr_t paddr)
{
/* calcuate index into page table */
unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
pgtable->pgtable[index] = paddr;
}
static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain)
{
u32 *p;
int ret, i;
etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
SZ_4K,
&etnaviv_domain->bad_page_dma,
GFP_KERNEL);
if (!etnaviv_domain->bad_page_cpu)
return -ENOMEM;
p = etnaviv_domain->bad_page_cpu;
for (i = 0; i < SZ_4K / 4; i++)
*p++ = 0xdead55aa;
ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE);
if (ret < 0) {
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
etnaviv_domain->bad_page_cpu,
etnaviv_domain->bad_page_dma);
return ret;
}
for (i = 0; i < PT_ENTRIES; i++)
etnaviv_domain->pgtable.pgtable[i] =
etnaviv_domain->bad_page_dma;
spin_lock_init(&etnaviv_domain->map_lock);
return 0;
}
static void etnaviv_domain_free(struct iommu_domain *domain)
{
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
pgtable_free(&etnaviv_domain->pgtable, PT_SIZE);
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
etnaviv_domain->bad_page_cpu,
etnaviv_domain->bad_page_dma);
kfree(etnaviv_domain);
}
static int etnaviv_iommuv1_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
if (size != SZ_4K)
return -EINVAL;
spin_lock(&etnaviv_domain->map_lock);
pgtable_write(&etnaviv_domain->pgtable, iova, paddr);
spin_unlock(&etnaviv_domain->map_lock);
return 0;
}
static size_t etnaviv_iommuv1_unmap(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
if (size != SZ_4K)
return -EINVAL;
spin_lock(&etnaviv_domain->map_lock);
pgtable_write(&etnaviv_domain->pgtable, iova,
etnaviv_domain->bad_page_dma);
spin_unlock(&etnaviv_domain->map_lock);
return SZ_4K;
}
static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,
dma_addr_t iova)
{
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
return pgtable_read(&etnaviv_domain->pgtable, iova);
}
static size_t etnaviv_iommuv1_dump_size(struct iommu_domain *domain)
{
return PT_SIZE;
}
static void etnaviv_iommuv1_dump(struct iommu_domain *domain, void *buf)
{
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
memcpy(buf, etnaviv_domain->pgtable.pgtable, PT_SIZE);
}
static struct etnaviv_iommu_ops etnaviv_iommu_ops = {
.ops = {
.domain_free = etnaviv_domain_free,
.map = etnaviv_iommuv1_map,
.unmap = etnaviv_iommuv1_unmap,
.iova_to_phys = etnaviv_iommu_iova_to_phys,
.pgsize_bitmap = SZ_4K,
},
.dump_size = etnaviv_iommuv1_dump_size,
.dump = etnaviv_iommuv1_dump,
};
void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
struct iommu_domain *domain)
{
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
u32 pgtable;
/* set page table address in MC */
pgtable = (u32)etnaviv_domain->pgtable.paddr;
gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable);
gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable);
gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
}
struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
{
struct etnaviv_iommu_domain *etnaviv_domain;
int ret;
etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
if (!etnaviv_domain)
return NULL;
etnaviv_domain->dev = gpu->dev;
etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops;
etnaviv_domain->domain.geometry.aperture_start = GPU_MEM_START;
etnaviv_domain->domain.geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1;
ret = __etnaviv_iommu_init(etnaviv_domain);
if (ret)
goto out_free;
return &etnaviv_domain->domain;
out_free:
kfree(etnaviv_domain);
return NULL;
}
/*
* Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETNAVIV_IOMMU_H__
#define __ETNAVIV_IOMMU_H__
#include <linux/iommu.h>
struct etnaviv_gpu;
struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu);
void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
struct iommu_domain *domain);
struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
#endif /* __ETNAVIV_IOMMU_H__ */
/*
* Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include "etnaviv_gpu.h"
#include "etnaviv_iommu.h"
#include "state_hi.xml.h"
struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu)
{
/* TODO */
return NULL;
}
/*
* Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETNAVIV_IOMMU_V2_H__
#define __ETNAVIV_IOMMU_V2_H__
#include <linux/iommu.h>
struct etnaviv_gpu;
struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
#endif /* __ETNAVIV_IOMMU_V2_H__ */
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "etnaviv_drv.h"
#include "etnaviv_gem.h"
#include "etnaviv_gpu.h"
#include "etnaviv_mmu.h"
static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
unsigned long iova, int flags, void *arg)
{
DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
return 0;
}
int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
struct sg_table *sgt, unsigned len, int prot)
{
struct iommu_domain *domain = iommu->domain;
struct scatterlist *sg;
unsigned int da = iova;
unsigned int i, j;
int ret;
if (!domain || !sgt)
return -EINVAL;
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
u32 pa = sg_dma_address(sg) - sg->offset;
size_t bytes = sg_dma_len(sg) + sg->offset;
VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
ret = iommu_map(domain, da, pa, bytes, prot);
if (ret)
goto fail;
da += bytes;
}
return 0;
fail:
da = iova;
for_each_sg(sgt->sgl, sg, i, j) {
size_t bytes = sg_dma_len(sg) + sg->offset;
iommu_unmap(domain, da, bytes);
da += bytes;
}
return ret;
}
int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
struct sg_table *sgt, unsigned len)
{
struct iommu_domain *domain = iommu->domain;
struct scatterlist *sg;
unsigned int da = iova;
int i;
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
size_t bytes = sg_dma_len(sg) + sg->offset;
size_t unmapped;
unmapped = iommu_unmap(domain, da, bytes);
if (unmapped < bytes)
return unmapped;
VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
BUG_ON(!PAGE_ALIGNED(bytes));
da += bytes;
}
return 0;
}
static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu,
struct etnaviv_vram_mapping *mapping)
{
struct etnaviv_gem_object *etnaviv_obj = mapping->object;
etnaviv_iommu_unmap(mmu, mapping->vram_node.start,
etnaviv_obj->sgt, etnaviv_obj->base.size);
drm_mm_remove_node(&mapping->vram_node);
}
int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
struct etnaviv_vram_mapping *mapping)
{
struct etnaviv_vram_mapping *free = NULL;
struct sg_table *sgt = etnaviv_obj->sgt;
struct drm_mm_node *node;
int ret;
lockdep_assert_held(&etnaviv_obj->lock);
mutex_lock(&mmu->lock);
/* v1 MMU can optimize single entry (contiguous) scatterlists */
if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) {
u32 iova;
iova = sg_dma_address(sgt->sgl) - memory_base;
if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
mapping->iova = iova;
list_add_tail(&mapping->mmu_node, &mmu->mappings);
mutex_unlock(&mmu->lock);
return 0;
}
}
node = &mapping->vram_node;
while (1) {
struct etnaviv_vram_mapping *m, *n;
struct list_head list;
bool found;
ret = drm_mm_insert_node_in_range(&mmu->mm, node,
etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL,
DRM_MM_SEARCH_DEFAULT);
if (ret != -ENOSPC)
break;
/*
* If we did not search from the start of the MMU region,
* try again in case there are free slots.
*/
if (mmu->last_iova) {
mmu->last_iova = 0;
mmu->need_flush = true;
continue;
}
/* Try to retire some entries */
drm_mm_init_scan(&mmu->mm, etnaviv_obj->base.size, 0, 0);
found = 0;
INIT_LIST_HEAD(&list);
list_for_each_entry(free, &mmu->mappings, mmu_node) {
/* If this vram node has not been used, skip this. */
if (!free->vram_node.mm)
continue;
/*
* If the iova is pinned, then it's in-use,
* so we must keep its mapping.
*/
if (free->use)
continue;
list_add(&free->scan_node, &list);
if (drm_mm_scan_add_block(&free->vram_node)) {
found = true;
break;
}
}
if (!found) {
/* Nothing found, clean up and fail */
list_for_each_entry_safe(m, n, &list, scan_node)
BUG_ON(drm_mm_scan_remove_block(&m->vram_node));
break;
}
/*
* drm_mm does not allow any other operations while
* scanning, so we have to remove all blocks first.
* If drm_mm_scan_remove_block() returns false, we
* can leave the block pinned.
*/
list_for_each_entry_safe(m, n, &list, scan_node)
if (!drm_mm_scan_remove_block(&m->vram_node))
list_del_init(&m->scan_node);
/*
* Unmap the blocks which need to be reaped from the MMU.
* Clear the mmu pointer to prevent the get_iova finding
* this mapping.
*/
list_for_each_entry_safe(m, n, &list, scan_node) {
etnaviv_iommu_remove_mapping(mmu, m);
m->mmu = NULL;
list_del_init(&m->mmu_node);
list_del_init(&m->scan_node);
}
/*
* We removed enough mappings so that the new allocation will
* succeed. Ensure that the MMU will be flushed before the
* associated commit requesting this mapping, and retry the
* allocation one more time.
*/
mmu->need_flush = true;
}
if (ret < 0) {
mutex_unlock(&mmu->lock);
return ret;
}
mmu->last_iova = node->start + etnaviv_obj->base.size;
mapping->iova = node->start;
ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size,
IOMMU_READ | IOMMU_WRITE);
if (ret < 0) {
drm_mm_remove_node(node);
mutex_unlock(&mmu->lock);
return ret;
}
list_add_tail(&mapping->mmu_node, &mmu->mappings);
mutex_unlock(&mmu->lock);
return ret;
}
void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
struct etnaviv_vram_mapping *mapping)
{
WARN_ON(mapping->use);
mutex_lock(&mmu->lock);
/* If the vram node is on the mm, unmap and remove the node */
if (mapping->vram_node.mm == &mmu->mm)
etnaviv_iommu_remove_mapping(mmu, mapping);
list_del(&mapping->mmu_node);
mutex_unlock(&mmu->lock);
}
void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
{
drm_mm_takedown(&mmu->mm);
iommu_domain_free(mmu->domain);
kfree(mmu);
}
struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu,
struct iommu_domain *domain, enum etnaviv_iommu_version version)
{
struct etnaviv_iommu *mmu;
mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
if (!mmu)
return ERR_PTR(-ENOMEM);
mmu->domain = domain;
mmu->gpu = gpu;
mmu->version = version;
mutex_init(&mmu->lock);
INIT_LIST_HEAD(&mmu->mappings);
drm_mm_init(&mmu->mm, domain->geometry.aperture_start,
domain->geometry.aperture_end -
domain->geometry.aperture_start + 1);
iommu_set_fault_handler(domain, etnaviv_fault_handler, gpu->dev);
return mmu;
}
size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu)
{
struct etnaviv_iommu_ops *ops;
ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops);
return ops->dump_size(iommu->domain);
}
void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf)
{
struct etnaviv_iommu_ops *ops;
ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops);
ops->dump(iommu->domain, buf);
}
/*
* Copyright (C) 2015 Etnaviv Project
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETNAVIV_MMU_H__
#define __ETNAVIV_MMU_H__
#include <linux/iommu.h>
enum etnaviv_iommu_version {
ETNAVIV_IOMMU_V1 = 0,
ETNAVIV_IOMMU_V2,
};
struct etnaviv_gpu;
struct etnaviv_vram_mapping;
struct etnaviv_iommu_ops {
struct iommu_ops ops;
size_t (*dump_size)(struct iommu_domain *);
void (*dump)(struct iommu_domain *, void *);
};
struct etnaviv_iommu {
struct etnaviv_gpu *gpu;
struct iommu_domain *domain;
enum etnaviv_iommu_version version;
/* memory manager for GPU address area */
struct mutex lock;
struct list_head mappings;
struct drm_mm mm;
u32 last_iova;
bool need_flush;
};
struct etnaviv_gem_object;
int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
int cnt);
int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
struct sg_table *sgt, unsigned len, int prot);
int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
struct sg_table *sgt, unsigned len);
int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
struct etnaviv_vram_mapping *mapping);
void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
struct etnaviv_vram_mapping *mapping);
void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu);
void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf);
struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu,
struct iommu_domain *domain, enum etnaviv_iommu_version version);
#endif /* __ETNAVIV_MMU_H__ */
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