/* linux/drivers/media/video/samsung/s3c_fimc_cfg.c
*
* Configuration support file for Samsung Camera Interface (FIMC) driver
*
*
*
* 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.
*/
#include <linux/slab.h>
#include <linux/bootmem.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <plat/media.h>
#include "s3c_fimc.h"
#if (CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMC > 0)
static dma_addr_t s3c_fimc_get_dma_region(u32 bytes)
{
dma_addr_t end, addr, *curr;
end = s3c_fimc.dma_start + s3c_fimc.dma_total;
curr = &s3c_fimc.dma_current;
if (*curr + bytes > end) {
addr = 0;
} else {
addr = *curr;
*curr += bytes;
}
return addr;
}
static void s3c_fimc_put_dma_region(u32 bytes)
{
s3c_fimc.dma_current -= bytes;
}
void s3c_fimc_free_output_memory(struct s3c_fimc_out_frame *info)
{
struct s3c_fimc_frame_addr *frame;
int i;
for (i = 0; i < info->nr_frames; i++) {
frame = &info->addr[i];
if (frame->phys_y)
s3c_fimc_put_dma_region(info->buf_size);
memset(frame, 0, sizeof(*frame));
}
info->buf_size = 0;
}
static int s3c_fimc_alloc_rgb_memory(struct s3c_fimc_out_frame *info)
{
struct s3c_fimc_frame_addr *frame;
int i, ret, nr_frames = info->nr_frames;
for (i = 0; i < nr_frames; i++) {
frame = &info->addr[i];
frame->phys_rgb = s3c_fimc_get_dma_region(info->buf_size);
if (frame->phys_rgb == 0) {
ret = -ENOMEM;
goto alloc_fail;
}
frame->virt_rgb = phys_to_virt(frame->phys_rgb);
}
for (i = nr_frames; i < S3C_FIMC_MAX_FRAMES; i++) {
frame = &info->addr[i];
frame->phys_rgb = info->addr[i - nr_frames].phys_rgb;
frame->virt_rgb = info->addr[i - nr_frames].virt_rgb;
}
return 0;
alloc_fail:
s3c_fimc_free_output_memory(info);
return ret;
}
static int s3c_fimc_alloc_yuv_memory(struct s3c_fimc_out_frame *info)
{
struct s3c_fimc_frame_addr *frame;
int i, ret, nr_frames = info->nr_frames;
u32 size = info->width * info->height, cbcr_size;
if (info->format == FORMAT_YCBCR420)
cbcr_size = size / 4;
else
cbcr_size = size / 2;
for (i = 0; i < nr_frames; i++) {
frame = &info->addr[i];
frame->phys_y = s3c_fimc_get_dma_region(info->buf_size);
if (frame->phys_y == 0) {
ret = -ENOMEM;
goto alloc_fail;
}
frame->phys_cb = frame->phys_y + size;
frame->phys_cr = frame->phys_cb + cbcr_size;
frame->virt_y = phys_to_virt(frame->phys_y);
frame->virt_cb = frame->virt_y + size;
frame->virt_cr = frame->virt_cb + cbcr_size;
}
for (i = nr_frames; i < S3C_FIMC_MAX_FRAMES; i++) {
frame = &info->addr[i];
frame->phys_y = info->addr[i - nr_frames].phys_y;
frame->phys_cb = info->addr[i - nr_frames].phys_cb;
frame->phys_cr = info->addr[i - nr_frames].phys_cr;
frame->virt_y = info->addr[i - nr_frames].virt_y;
frame->virt_cb = info->addr[i - nr_frames].virt_cb;
frame->virt_cr = info->addr[i - nr_frames].virt_cr;
}
return 0;
alloc_fail:
s3c_fimc_free_output_memory(info);
return ret;
}
#else
void s3c_fimc_free_output_memory(struct s3c_fimc_out_frame *info)
{
struct s3c_fimc_frame_addr *frame;
int i;
for (i = 0; i < info->nr_frames; i++) {
frame = &info->addr[i];
if (frame->virt_y)
kfree(frame->virt_y);
memset(frame, 0, sizeof(*frame));
}
info->buf_size = 0;
}
static int s3c_fimc_alloc_rgb_memory(struct s3c_fimc_out_frame *info)
{
struct s3c_fimc_frame_addr *frame;
int i, ret, nr_frames = info->nr_frames;
for (i = 0; i < nr_frames; i++) {
frame = &info->addr[i];
frame->virt_rgb = kmalloc(info->buf_size, GFP_DMA);
if (frame->virt_rgb == NULL) {
ret = -ENOMEM;
goto alloc_fail;
}
frame->phys_rgb = virt_to_phys(frame->virt_rgb);
}
for (i = nr_frames; i < S3C_FIMC_MAX_FRAMES; i++) {
frame = &info->addr[i];
frame->virt_rgb = info->addr[i - nr_frames].virt_rgb;
frame->phys_rgb = info->addr[i - nr_frames].phys_rgb;
}
return 0;
alloc_fail:
s3c_fimc_free_output_memory(info);
return ret;
}
static int s3c_fimc_alloc_yuv_memory(struct s3c_fimc_out_frame *info)
{
struct s3c_fimc_frame_addr *frame;
int i, ret, nr_frames = info->nr_frames;
u32 size = info->width * info->height, cbcr_size;
if (info->format == FORMAT_YCBCR420)
cbcr_size = size / 4;
else
cbcr_size = size / 2;
for (i = 0; i < nr_frames; i++) {
frame = &info->addr[i];
frame->virt_y = kmalloc(info->buf_size, GFP_DMA);
if (frame->virt_y == NULL) {
ret = -ENOMEM;
goto alloc_fail;
}
frame->virt_cb = frame->virt_y + size;
frame->virt_cr = frame->virt_cb + cbcr_size;
frame->phys_y = virt_to_phys(frame->virt_y);
frame->phys_cb = frame->phys_y + size;
frame->phys_cr = frame->phys_cb + cbcr_size;
}
for (i = nr_frames; i < S3C_FIMC_MAX_FRAMES; i++) {
frame = &info->addr[i];
frame->phys_y = info->addr[i - nr_frames].phys_y;
frame->phys_cb = info->addr[i - nr_frames].phys_cb;
frame->phys_cr = info->addr[i - nr_frames].phys_cr;
frame->virt_y = info->addr[i - nr_frames].virt_y;
frame->virt_cb = info->addr[i - nr_frames].virt_cb;
frame->virt_cr = info->addr[i - nr_frames].virt_cr;
}
return 0;
alloc_fail:
s3c_fimc_free_output_memory(info);
return ret;
}
#endif
static u32 s3c_fimc_get_buffer_size(int width, int height, enum s3c_fimc_format_t fmt)
{
u32 size = width * height;
u32 cbcr_size = 0, *buf_size = NULL, one_p_size;
switch (fmt) {
case FORMAT_RGB565:
size *= 2;
buf_size = &size;
break;
case FORMAT_RGB666: /* fall through */
case FORMAT_RGB888:
size *= 4;
buf_size = &size;
break;
case FORMAT_YCBCR420:
cbcr_size = size / 4;
one_p_size = size + (2 * cbcr_size);
buf_size = &one_p_size;
break;
case FORMAT_YCBCR422:
cbcr_size = size / 2;
one_p_size = size + (2 * cbcr_size);
buf_size = &one_p_size;
break;
}
if (*buf_size % PAGE_SIZE != 0)
*buf_size = (*buf_size / PAGE_SIZE + 1) * PAGE_SIZE;
return *buf_size;
}
int s3c_fimc_alloc_output_memory(struct s3c_fimc_out_frame *info)
{
int ret;
info->buf_size = s3c_fimc_get_buffer_size(info->width, info->height, \
info->format);
printk("s3c_fimc_alloc_output_memory width %x height %x format %x buf_size = %x\n",info->width, info->height, info->format,info->buf_size);
if (info->format == FORMAT_YCBCR420 || info->format == FORMAT_YCBCR422)
ret = s3c_fimc_alloc_yuv_memory(info);
else
ret = s3c_fimc_alloc_rgb_memory(info);
return ret;
}
int s3c_fimc_alloc_input_memory(struct s3c_fimc_in_frame *info, dma_addr_t addr)
{
struct s3c_fimc_frame_addr *frame;
u32 size = info->width * info->height, cbcr_size;
if (info->format == FORMAT_YCBCR420)
cbcr_size = size / 4;
else
cbcr_size = size / 2;
info->buf_size = s3c_fimc_get_buffer_size(info->width, info->height, \
info->format);
switch (info->format) {
case FORMAT_RGB565: /* fall through */
case FORMAT_RGB666: /* fall through */
case FORMAT_RGB888:
info->addr.phys_rgb = addr;
break;
case FORMAT_YCBCR420: /* fall through */
case FORMAT_YCBCR422:
frame = &info->addr;
frame->phys_y = addr;
frame->phys_cb = frame->phys_y + size;
frame->phys_cr = frame->phys_cb + cbcr_size;
break;
}
return 0;
}
int s3c_fimc_alloc_y_memory(struct s3c_fimc_in_frame *info,
dma_addr_t addr)
{
info->addr.phys_y = addr;
info->buf_size = s3c_fimc_get_buffer_size(info->width, \
info->height, info->format);
return 0;
}
int s3c_fimc_alloc_cb_memory(struct s3c_fimc_in_frame *info,
dma_addr_t addr)
{
info->addr.phys_cb = addr;
info->buf_size = s3c_fimc_get_buffer_size(info->width, \
info->height, info->format);
return 0;
}
int s3c_fimc_alloc_cr_memory(struct s3c_fimc_in_frame *info,
dma_addr_t addr)
{
info->addr.phys_cr = addr;
info->buf_size = s3c_fimc_get_buffer_size(info->width, \
info->height, info->format);
return 0;
}
void s3c_fimc_set_nr_frames(struct s3c_fimc_control *ctrl, int nr)
{
if (nr == 3)
ctrl->out_frame
评论0