diff options
Diffstat (limited to 'drivers/staging/dt3155v4l/dt3155-bufs.c')
-rw-r--r-- | drivers/staging/dt3155v4l/dt3155-bufs.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/drivers/staging/dt3155v4l/dt3155-bufs.c b/drivers/staging/dt3155v4l/dt3155-bufs.c new file mode 100644 index 000000000000..f93e431e786b --- /dev/null +++ b/drivers/staging/dt3155v4l/dt3155-bufs.c @@ -0,0 +1,256 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Marin Mitov * + * mitov@issp.bas.bg * + * * + * 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; either version 2 of the License, or * + * (at your option) any later version. * + * * + * 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "dt3155-bufs.h" + +/** + * dt3155_init_chunks_buf - creates a chunk buffer and allocates memory for it + * + * returns: a pointer to the struct dt3155_buf or NULL if failed + * + * Creates a struct dt3155_buf, then allocates a chunk of memory of + * size DT3155_CHUNK_SIZE and sets all the pages in it as Reserved. + * This is done to be able to use remap_pfn_range() on these buffers + * (which do not work on normal memory if Reserved bit is not set) + */ +struct dt3155_buf * +dt3155_init_chunks_buf(void) +{ /* could sleep */ + struct dt3155_buf *buf; + int i; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + buf->cpu = (void *)__get_free_pages(DT3155_CHUNK_FLAGS, + get_order(DT3155_CHUNK_SIZE)); + if (!buf->cpu) { + kfree(buf); + return NULL; + } + for (i = 0; i < DT3155_CHUNK_SIZE; i += PAGE_SIZE) + SetPageReserved(virt_to_page(buf->cpu + i)); + return buf; /* success */ +} + +/** + * dt3155_free_chunks_buf - destroys the specified buffer + * + * @buf: the buffer to be freed + * + * Clears Reserved bit of all pages in the chunk, frees the chunk memory + * and destroys struct dt3155_buf. + */ +void +dt3155_free_chunks_buf(struct dt3155_buf *buf) +{ + int i; + + for (i = 0; i < DT3155_CHUNK_SIZE; i += PAGE_SIZE) + ClearPageReserved(virt_to_page(buf->cpu + i)); + free_pages((unsigned long)buf->cpu, get_order(DT3155_CHUNK_SIZE)); + kfree(buf); +} + +/** + * dt3155_init_fifo - creates and initializes a fifo + * + * returns: a pointer to the crated and initialized struct dt3155_fifo + * or NULL if failed + */ +struct dt3155_fifo * +dt3155_init_fifo(void) +{ /* could sleep */ + struct dt3155_fifo *fifo = kzalloc(sizeof(*fifo), GFP_KERNEL); + if (fifo) + spin_lock_init(&fifo->lock); + return fifo; +} + +/* dt3155_free_fifo(x) defined as macro in dt3155.h */ + +/** + * dt3155_get_buf - gets a buffer from the fifo + * + * @fifo: the fifo to get a buffer from + * + * returns: a pointer to the buffer or NULL if failed + * + * dt3155_get_buf gets the fifo's spin_lock and returns the + * buffer pointed by the head. Could be used in any context. + */ +struct dt3155_buf * +dt3155_get_buf(struct dt3155_fifo *fifo) +{ + unsigned long flags; + struct dt3155_buf *tmp_buf; + + spin_lock_irqsave(&fifo->lock, flags); + tmp_buf = fifo->head; + if (fifo->head) + fifo->head = fifo->head->next; + if (!fifo->head) + fifo->tail = NULL; + spin_unlock_irqrestore(&fifo->lock, flags); + return tmp_buf; +} + +/** + * dt3155_put_buf - puts a buffer into a fifo + * + * @buf: the buffer to put + * @fifo: the fifo to put the buffer in + * + * dt3155_put_buf gets the fifo's spin_lock and puts the buf + * at the tail of the fifo. Could be used in any context. + */ +void +dt3155_put_buf(struct dt3155_buf *buf, struct dt3155_fifo *fifo) +{ + unsigned long flags; + + spin_lock_irqsave(&fifo->lock, flags); + buf->next = NULL; + if (fifo->tail) + fifo->tail->next = buf; + fifo->tail = buf; + if (!fifo->head) + fifo->head = buf; + spin_unlock_irqrestore(&fifo->lock, flags); +} + +/** + * dt3155_init_chunks_fifo - creates and fills a chunks_fifo + * + * returns: a pointer to the fifo or NULL if failed + * + * dt3155_init_chunks_fifo creates and fills the fifo with + * a number of chunks <= DT3155_CHUNK_NUM. The returned fifo + * contains at least one chunk. + */ +struct dt3155_fifo * +dt3155_init_chunks_fifo(void) +{ /* could sleep */ + int i; + + struct dt3155_fifo *chunks; + struct dt3155_buf *tmp_buf; + + chunks = dt3155_init_fifo(); + if (!chunks) + return NULL; + tmp_buf = dt3155_init_chunks_buf(); + if (!tmp_buf) { + dt3155_free_fifo(chunks); + return NULL; + } + dt3155_put_buf(tmp_buf, chunks); + for (i = 1; i < DT3155_CHUNK_NUM; i++) { + tmp_buf = dt3155_init_chunks_buf(); + if (!tmp_buf) + break; + dt3155_put_buf(tmp_buf, chunks); + } + return chunks; +} + +/** + * dt3155_free_chunks_fifo - empties and destroys the chunks_fifo + * + * @chunks: the chunks_fifo to be freed + * + * dt3155_free_chunks_fifo deallocates all chunks in the fifo and + * destroys it. + */ +void +dt3155_free_chunks_fifo(struct dt3155_fifo *chunks) +{ + int buf_count = 0; + struct dt3155_buf *buf; + + while ((buf = dt3155_get_buf(chunks))) { + dt3155_free_chunks_buf(buf); + buf_count++; + } + dt3155_free_fifo(chunks); + printk(KERN_INFO "dt3155: %i chunks freed\n", buf_count); +} + +/** + * dt3155_init_ibufs_fifo - creates and fills an image buffer fifo + * + * @chunks: chunks_fifo to take memory from + * @buf_size: the size of image buffers + * + * returns: a pointer to the fifo filled with image buffers + * + * dt3155_init_ibufs_fifo takes chunks from chunks_fifo, chops them + * into pieces of size buf_size and fills image fifo with them. + */ +struct dt3155_fifo * +dt3155_init_ibufs_fifo(struct dt3155_fifo *chunks, int buf_size) +{ /* could sleep */ + int i, buf_count = 0; + struct dt3155_buf *tmp_ibuf, *chunks_buf, *last_chunk; + struct dt3155_fifo *tmp_fifo; + + tmp_fifo = dt3155_init_fifo(); + if (!tmp_fifo) + return NULL; + last_chunk = chunks->tail; + do { + chunks_buf = dt3155_get_buf(chunks); + dt3155_put_buf(chunks_buf, chunks); + for (i = 0; i < DT3155_CHUNK_SIZE / buf_size; i++) { + tmp_ibuf = kzalloc(sizeof(*tmp_ibuf), GFP_KERNEL); + if (tmp_ibuf) { + tmp_ibuf->cpu = + chunks_buf->cpu + DT3155_BUF_SIZE * i; + dt3155_put_buf(tmp_ibuf, tmp_fifo); + buf_count++; + } else { + if (buf_count) { + goto print_num_bufs; + } else { + dt3155_free_fifo(tmp_fifo); + return NULL; + } + } + } + } while (chunks_buf != last_chunk); +print_num_bufs: + printk(KERN_INFO "dt3155: %i image buffers available\n", buf_count); + return tmp_fifo; +} + +/** + * dt3155_free_ibufs_fifo - empties and destroys an image fifo + * + * @fifo: the fifo to free + */ +void +dt3155_free_ibufs_fifo(struct dt3155_fifo *fifo) +{ + struct dt3155_buf *tmp_ibuf; + + while ((tmp_ibuf = dt3155_get_buf(fifo))) + kfree(tmp_ibuf); + kfree(fifo); +} |