/*
* DMM IOMMU driver support functions for TI OMAP processors.
*
* Author: Rob Clark <rob@ti.com>
* Andy Gross <andy.gross@ti.com>
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> /* platform_device() */
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/list.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;
/* 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 {
uint32_t x_shft; /* unused X-bits (as part of bpp) */
uint32_t y_shft; /* unused Y-bits (as part of bpp) */
uint32_t cpp; /* bytes/chars per pixel */
uint32_t slot_w; /* width of each slot (in pixels) */
uint32_t 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 uint32_t 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},
};
/* simple allocator to grab next 16 byte aligned memory from txn */
static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa)
{
void *ptr;
struct refill_engine *engine = txn->engine_handle;
/* dmm programming requires 16 byte aligned addresses */
txn->current_pa = round_up(txn->current_pa, 16);
txn->current_va = (void *)round_up((long)txn->current_va, 16);
ptr = txn->current_va;
*pa = txn->current_pa;
txn->current_pa += sz;
txn->current_va += sz;
BUG_ON((txn->current_va - engine->refill_va) > REFILL_BUFFER_SIZE);
return ptr;
}
/* check status and spin until wait_mask comes true */
static int wait_status(struct refill_engine *engine, uint32_t wait_mask)
{
struct dmm *dmm = engine->dmm;
uint32_t r = 0, err, i;
i = DMM_FIXED_RETRY_COUNT;
while (true) {
r = readl(dmm->base + reg[PAT_STATUS][engine->id]);
err = r & DMM_PATSTATUS_ERR;
if (err)
return -EFAULT;
if ((r & wait_mask) == wait_mask)
break;
if (--i == 0)
return -ETIMEDOUT;
udelay(1);
}
return 0;
}
static void release_engine(struct refill_engine *engine)
{
unsigned long flags;
spin_lock_irqsave(&list_lock, flags);
list_add(&engine->idle_node, &omap_dmm->idle_head);
spin_unlock_irqrestore(&list_lock, flags);
atomic_inc(&omap_dmm->engine_counter);
wake_up_interruptible(&omap_dmm->engine_queue);
}
static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
{
struct dmm *dmm = arg;
uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS);
int i;
/* ack IRQ */
writel(status, dmm->base + DMM_PAT_IRQSTATUS);
for (i = 0; i < dmm->num_engines; i++) {
if (status & DMM_IRQSTAT_LST) {
wake_up_interruptible(&dmm->engines[i].wait_for_refill);
if (dmm->engines[i].async)
release_engine(&dmm->engines[i]);
}
status >>= 8;
}
return IRQ_HANDLED;
}
/**
* Get a handle for a DMM transaction
*/
static struct dmm_txn *dmm_txn_init<