linux/drivers/staging/dt3155/dt3155_isr.c

510 lines
15 KiB
C
Raw Normal View History

/*
Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
Jason Lapenta, Scott Smedley, Greg Sharp
This file is part of the DT3155 Device Driver.
The DT3155 Device Driver 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; either version 2 of the
License, or (at your option) any later version.
The DT3155 Device Driver 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 the DT3155 Device Driver; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
File: dt3155_isr.c
Purpose: Buffer management routines, and other routines for the ISR
(the actual isr is in dt3155_drv.c)
-- Changes --
Date Programmer Description of changes made
-------------------------------------------------------------------
03-Jul-2000 JML n/a
02-Apr-2002 SS Mods to make work with separate allocator
module; Merged John Roll's mods to make work with
multiple boards.
10-Jul-2002 GCS Complete rewrite of setup_buffers to disallow
buffers which span a 4MB boundary.
24-Jul-2002 SS GPL licence.
30-Jul-2002 NJC Added support for buffer loop.
31-Jul-2002 NJC Complete rewrite of buffer management
02-Aug-2002 NJC Including slab.h instead of malloc.h (no warning).
Also, allocator_init() now returns allocator_max
so cleaned up allocate_buffers() accordingly.
08-Aug-2005 SS port to 2.6 kernel.
*/
#include <asm/system.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
#include <linux/gfp.h>
#include <linux/sched.h>
#include <linux/types.h>
#include "dt3155.h"
#include "dt3155_drv.h"
#include "dt3155_io.h"
#include "dt3155_isr.h"
#include "allocator.h"
#define FOUR_MB (0x0400000) /* Can't DMA accross a 4MB boundary!*/
#define UPPER_10_BITS (0x3FF<<22) /* Can't DMA accross a 4MB boundary!*/
/* Pointer into global structure for handling buffers */
struct dt3155_fbuffer *dt3155_fbuffer[MAXBOARDS] = {NULL
#if MAXBOARDS == 2
, NULL
#endif
};
/******************************************************************************
* Simple array based que struct
*
* Some handy functions using the buffering structure.
*****************************************************************************/
/***************************
* are_empty_buffers
* m is minor # of device
***************************/
bool are_empty_buffers(int m)
{
return dt3155_fbuffer[m]->empty_len;
}
/**************************
* push_empty
* m is minor # of device
*
* This is slightly confusing. The number empty_len is the literal #
* of empty buffers. After calling, empty_len-1 is the index into the
* empty buffer stack. So, if empty_len == 1, there is one empty buffer,
* given by dt3155_fbuffer[m]->empty_buffers[0].
* empty_buffers should never fill up, though this is not checked.
**************************/
void push_empty(int index, int m)
{
dt3155_fbuffer[m]->empty_buffers[dt3155_fbuffer[m]->empty_len] = index;
dt3155_fbuffer[m]->empty_len++;
}
/**************************
* pop_empty(m)
* m is minor # of device
**************************/
int pop_empty(int m)
{
dt3155_fbuffer[m]->empty_len--;
return dt3155_fbuffer[m]->empty_buffers[dt3155_fbuffer[m]->empty_len];
}
/*************************
* is_ready_buf_empty(m)
* m is minor # of device
*************************/
bool is_ready_buf_empty(int m)
{
return ((dt3155_fbuffer[m]->ready_len) == 0);
}
/*************************
* is_ready_buf_full(m)
* m is minor # of device
* this should *never* be true if there are any active, locked or empty
* buffers, since it corresponds to nbuffers ready buffers!!
* 7/31/02: total rewrite. --NJC
*************************/
bool is_ready_buf_full(int m)
{
return dt3155_fbuffer[m]->ready_len == dt3155_fbuffer[m]->nbuffers;
}
/*****************************************************
* push_ready(m, buffer)
* m is minor # of device
*
*****************************************************/
void push_ready(int m, int index)
{
int head = dt3155_fbuffer[m]->ready_head;
dt3155_fbuffer[m]->ready_que[head] = index;
dt3155_fbuffer[m]->ready_head = ((head + 1) %
(dt3155_fbuffer[m]->nbuffers));
dt3155_fbuffer[m]->ready_len++;
}
/*****************************************************
* get_tail()
* m is minor # of device
*
* Simply comptutes the tail given the head and the length.
*****************************************************/
static int get_tail(int m)
{
return (dt3155_fbuffer[m]->ready_head -
dt3155_fbuffer[m]->ready_len +
dt3155_fbuffer[m]->nbuffers)%
(dt3155_fbuffer[m]->nbuffers);
}
/*****************************************************
* pop_ready()
* m is minor # of device
*
* This assumes that there is a ready buffer ready... should
* be checked (e.g. with is_ready_buf_empty() prior to call.
*****************************************************/
int pop_ready(int m)
{
int tail;
tail = get_tail(m);
dt3155_fbuffer[m]->ready_len--;
return dt3155_fbuffer[m]->ready_que[tail];
}
/*****************************************************
* printques
* m is minor # of device
*****************************************************/
void printques(int m)
{
int head = dt3155_fbuffer[m]->ready_head;
int tail;
int num = dt3155_fbuffer[m]->nbuffers;
int frame_index;
int index;
tail = get_tail(m);
printk("\n R:");
for (index = tail; index != head; index++, index = index % (num)) {
frame_index = dt3155_fbuffer[m]->ready_que[index];
printk(" %d ", frame_index);
}
printk("\n E:");
for (index = 0; index < dt3155_fbuffer[m]->empty_len; index++) {
frame_index = dt3155_fbuffer[m]->empty_buffers[index];
printk(" %d ", frame_index);
}
frame_index = dt3155_fbuffer[m]->active_buf;
printk("\n A: %d", frame_index);
frame_index = dt3155_fbuffer[m]->locked_buf;
printk("\n L: %d\n", frame_index);
}
/*****************************************************
* adjust_4MB
*
* If a buffer intersects the 4MB boundary, push
* the start address up to the beginning of the
* next 4MB chunk (assuming bufsize < 4MB).
*****************************************************/
u32 adjust_4MB(u32 buf_addr, u32 bufsize)
{
if (((buf_addr+bufsize) & UPPER_10_BITS) != (buf_addr & UPPER_10_BITS))
return (buf_addr+bufsize) & UPPER_10_BITS;
else
return buf_addr;
}
/*****************************************************
* allocate_buffers
*
* Try to allocate enough memory for all requested
* buffers. If there is not enough free space
* try for less memory.
*****************************************************/
void allocate_buffers(u32 *buf_addr, u32* total_size_kbs,
u32 bufsize)
{
/* Compute the minimum amount of memory guaranteed to hold all
MAXBUFFERS such that no buffer crosses the 4MB boundary.
Store this value in the variable "full_size" */
u32 allocator_max;
u32 bufs_per_chunk = (FOUR_MB / bufsize);
u32 filled_chunks = (MAXBUFFERS-1) / bufs_per_chunk;
u32 leftover_bufs = MAXBUFFERS - filled_chunks * bufs_per_chunk;
u32 full_size = bufsize /* possibly unusable part of 1st chunk */
+ filled_chunks * FOUR_MB /* max # of completely filled 4mb chunks */
+ leftover_bufs * bufsize; /* these buffs will be in a partly filled
chunk at beginning or end */
u32 full_size_kbs = 1 + (full_size-1) / 1024;
u32 min_size_kbs = 2*ndevices*bufsize / 1024;
u32 size_kbs;
/* Now, try to allocate full_size. If this fails, keep trying for
less & less memory until it succeeds. */
#ifndef STANDALONE_ALLOCATOR
/* initialize the allocator */
allocator_init(&allocator_max);
#endif
size_kbs = full_size_kbs;
*buf_addr = 0;
printk("DT3155: We would like to get: %d KB\n", full_size_kbs);
printk("DT3155: ...but need at least: %d KB\n", min_size_kbs);
printk("DT3155: ...the allocator has: %d KB\n", allocator_max);
size_kbs = (full_size_kbs <= allocator_max ? full_size_kbs : allocator_max);
if (size_kbs > min_size_kbs) {
if ((*buf_addr = allocator_allocate_dma(size_kbs, GFP_KERNEL)) != 0) {
printk("DT3155: Managed to allocate: %d KB\n", size_kbs);
*total_size_kbs = size_kbs;
return;
}
}
/* If we got here, the allocation failed */
printk("DT3155: Allocator failed!\n");
*buf_addr = 0;
*total_size_kbs = 0;
return;
}
/*****************************************************
* dt3155_setup_buffers
*
* setup_buffers just puts the buffering system into
* a consistent state before the start of interrupts
*
* JML : it looks like all the buffers need to be
* continuous. So I'm going to try and allocate one
* continuous buffer.
*
* GCS : Fix DMA problems when buffer spans
* 4MB boundary. Also, add error checking. This
* function will return -ENOMEM when not enough memory.
*****************************************************/
u32 dt3155_setup_buffers(u32 *allocatorAddr)
{
u32 index;
u32 rambuff_addr; /* start of allocation */
u32 rambuff_size; /* total size allocated to driver */
u32 rambuff_acm; /* accumlator, keep track of how much
is left after being split up*/
u32 rambuff_end; /* end of rambuff */
u32 numbufs; /* number of useful buffers allocated (per device) */
u32 bufsize = DT3155_MAX_ROWS * DT3155_MAX_COLS;
int m; /* minor # of device, looped for all devs */
/* zero the fbuffer status and address structure */
for (m = 0; m < ndevices; m++) {
dt3155_fbuffer[m] = &(dt3155_status[m].fbuffer);
/* Make sure the buffering variables are consistent */
{
u8 *ptr = (u8 *) dt3155_fbuffer[m];
for (index = 0; index < sizeof(struct dt3155_fbuffer); index++)
*(ptr++) = 0;
}
}
/* allocate a large contiguous chunk of RAM */
allocate_buffers(&rambuff_addr, &rambuff_size, bufsize);
printk("DT3155: mem info\n");
printk(" - rambuf_addr = 0x%x\n", rambuff_addr);
printk(" - length (kb) = %u\n", rambuff_size);
if (rambuff_addr == 0) {
printk(KERN_INFO
"DT3155: Error setup_buffers() allocator dma failed\n");
return -ENOMEM;
}
*allocatorAddr = rambuff_addr;
rambuff_end = rambuff_addr + 1024 * rambuff_size;
/* after allocation, we need to count how many useful buffers there
are so we can give an equal number to each device */
rambuff_acm = rambuff_addr;
for (index = 0; index < MAXBUFFERS; index++) {
rambuff_acm = adjust_4MB(rambuff_acm, bufsize);/*avoid spanning 4MB bdry*/
if (rambuff_acm + bufsize > rambuff_end)
break;
rambuff_acm += bufsize;
}
/* Following line is OK, will waste buffers if index
* not evenly divisible by ndevices -NJC*/
numbufs = index / ndevices;
printk(" - numbufs = %u\n", numbufs);
if (numbufs < 2) {
printk(KERN_INFO
"DT3155: Error setup_buffers() couldn't allocate 2 bufs/board\n");
return -ENOMEM;
}
/* now that we have board memory we spit it up */
/* between the boards and the buffers */
rambuff_acm = rambuff_addr;
for (m = 0; m < ndevices; m++) {
rambuff_acm = adjust_4MB(rambuff_acm, bufsize);
/* Save the start of this boards buffer space (for mmap). */
dt3155_status[m].mem_addr = rambuff_acm;
for (index = 0; index < numbufs; index++) {
rambuff_acm = adjust_4MB(rambuff_acm, bufsize);
if (rambuff_acm + bufsize > rambuff_end) {
/* Should never happen */
printk("DT3155 PROGRAM ERROR (GCS)\n"
"Error distributing allocated buffers\n");
return -ENOMEM;
}
dt3155_fbuffer[m]->frame_info[index].addr = rambuff_acm;
push_empty(index, m);
/* printk(" - Buffer : %lx\n",
* dt3155_fbuffer[m]->frame_info[index].addr);
*/
dt3155_fbuffer[m]->nbuffers += 1;
rambuff_acm += bufsize;
}
/* Make sure there is an active buffer there. */
dt3155_fbuffer[m]->active_buf = pop_empty(m);
dt3155_fbuffer[m]->even_happened = 0;
dt3155_fbuffer[m]->even_stopped = 0;
/* make sure there is no locked_buf JML 2/28/00 */
dt3155_fbuffer[m]->locked_buf = -1;
dt3155_status[m].mem_size =
rambuff_acm - dt3155_status[m].mem_addr;
/* setup the ready queue */
dt3155_fbuffer[m]->ready_head = 0;
dt3155_fbuffer[m]->ready_len = 0;
printk("Available buffers for device %d: %d\n",
m, dt3155_fbuffer[m]->nbuffers);
}
return 1;
}
/*****************************************************
* internal_release_locked_buffer
*
* The internal function for releasing a locked buffer.
* It assumes interrupts are turned off.
*
* m is minor number of device
*****************************************************/
static void internal_release_locked_buffer(int m)
{
/* Pointer into global structure for handling buffers */
if (dt3155_fbuffer[m]->locked_buf >= 0) {
push_empty(dt3155_fbuffer[m]->locked_buf, m);
dt3155_fbuffer[m]->locked_buf = -1;
}
}
/*****************************************************
* dt3155_release_locked_buffer()
* m is minor # of device
*
* The user function of the above.
*
*****************************************************/
void dt3155_release_locked_buffer(int m)
{
unsigned long int flags;
local_save_flags(flags);
local_irq_disable();
internal_release_locked_buffer(m);
local_irq_restore(flags);
}
/*****************************************************
* dt3155_flush()
* m is minor # of device
*
*****************************************************/
int dt3155_flush(int m)
{
int index;
unsigned long int flags;
local_save_flags(flags);
local_irq_disable();
internal_release_locked_buffer(m);
dt3155_fbuffer[m]->empty_len = 0;
for (index = 0; index < dt3155_fbuffer[m]->nbuffers; index++)
push_empty(index, m);
/* Make sure there is an active buffer there. */
dt3155_fbuffer[m]->active_buf = pop_empty(m);
dt3155_fbuffer[m]->even_happened = 0;
dt3155_fbuffer[m]->even_stopped = 0;
/* setup the ready queue */
dt3155_fbuffer[m]->ready_head = 0;
dt3155_fbuffer[m]->ready_len = 0;
local_irq_restore(flags);
return 0;
}
/*****************************************************
* dt3155_get_ready_buffer()
* m is minor # of device
*
* get_ready_buffer will grab the next chunk of data
* if it is already there, otherwise it returns 0.
* If the user has a buffer locked it will unlock
* that buffer before returning the new one.
*****************************************************/
int dt3155_get_ready_buffer(int m)
{
int frame_index;
unsigned long int flags;
local_save_flags(flags);
local_irq_disable();
#ifdef DEBUG_QUES_A
printques(m);
#endif
internal_release_locked_buffer(m);
if (is_ready_buf_empty(m))
frame_index = -1;
else {
frame_index = pop_ready(m);
dt3155_fbuffer[m]->locked_buf = frame_index;
}
#ifdef DEBUG_QUES_B
printques(m);
#endif
local_irq_restore(flags);
return frame_index;
}