// SPDX-License-Identifier: GPL-2.0-only
/*
* DMM IOMMU driver support functions for TI OMAP processors.
*
* Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
* Author: Rob Clark <rob@ti.com>
* Andy Gross <andy.gross@ti.com>
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/platform_device.h> /* platform_device() */
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include "omap_dmm_tiler.h"
#include "omap_dmm_priv.h"
#define DMM_DRIVER_NAME "dmm"
/* mappings for associating views to luts */
static struct tcm *containers[TILFMT_NFORMATS];
static struct dmm *omap_dmm;
#if defined(CONFIG_OF)
static const struct of_device_id dmm_of_match[];
#endif
/* global spinlock for protecting lists */
static DEFINE_SPINLOCK(list_lock);
/* Geometry table */
#define GEOM(xshift, yshift, bytes_per_pixel) { \
.x_shft = (xshift), \
.y_shft = (yshift), \
.cpp = (bytes_per_pixel), \
.slot_w = 1 << (SLOT_WIDTH_BITS - (xshift)), \
.slot_h = 1 << (SLOT_HEIGHT_BITS - (yshift)), \
}
static const struct {
u32 x_shft; /* unused X-bits (as part of bpp) */
u32 y_shft; /* unused Y-bits (as part of bpp) */
u32 cpp; /* bytes/chars per pixel */
u32 slot_w; /* width of each slot (in pixels) */
u32 slot_h; /* height of each slot (in pixels) */
} geom[TILFMT_NFORMATS] = {
[TILFMT_8BIT] = GEOM(0, 0, 1),
[TILFMT_16BIT] = GEOM(0, 1, 2),
[TILFMT_32BIT] = GEOM(1, 1, 4),
[TILFMT_PAGE] = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
};
/* lookup table for registers w/ per-engine instances */
static const u32 reg[][4] = {
[PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
[PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
};
static int dmm_dma_copy(struct dmm *dmm, dma_addr_t src, dma_addr_t dst)
{
struct dma_async_tx_descriptor *tx;
enum dma_status status;
dma_cookie_t cookie;
tx = dmaengine_prep_dma_memcpy(dmm->wa_dma_chan, dst, src, 4, 0);
if (!tx) {
dev_err(dmm->dev, "Failed to prepare DMA memcpy\n");
return -EIO;
}
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
dev_err(dmm->dev, "Failed to do DMA tx_submit\n");
return -EIO;
}
status = dma_sync_wait(dmm->wa_dma_chan, cookie);
if (status != DMA_COMPLETE)
dev_err(dmm->dev, "i878 wa DMA copy failure\n");
dmaengine_terminate_all(dmm->wa_dma_chan);
return 0;
}
static u32 dmm_read_wa(struct dmm *dmm, u32 reg)
{
dma_addr_t src, dst;
int r;
src = dmm->phys_base + reg;
dst = dmm->wa_dma_handle;
r = dmm_dma_copy(dmm, src, dst);
if (r) {
dev_err(dmm->dev, "sDMA read transfer timeout\n");
return readl(dmm->base + reg);
}
/*
* As per i878 workaround, the DMA is used to access the DMM registers.
* Make sure that the readl is not moved by the compiler or the CPU
* earlier than the DMA finished writing the value to memory.
*/
rmb();
return readl(dmm->wa_dma_data);
}
static void dmm_write_wa(struct dmm *dmm, u32 val, u32 <