/*
* ispstat.c
*
* TI OMAP3 ISP - Statistics core
*
* Copyright (C) 2010 Nokia Corporation
* Copyright (C) 2009 Texas Instruments, Inc
*
* Contacts: David Cohen <dacohen@gmail.com>
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Sakari Ailus <sakari.ailus@iki.fi>
*
* 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.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "isp.h"
#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0)
/*
* MAGIC_SIZE must always be the greatest common divisor of
* AEWB_PACKET_SIZE and AF_PAXEL_SIZE.
*/
#define MAGIC_SIZE 16
#define MAGIC_NUM 0x55
/* HACK: AF module seems to be writing one more paxel data than it should. */
#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE
/*
* HACK: H3A modules go to an invalid state after have a SBL overflow. It makes
* the next buffer to start to be written in the same point where the overflow
* occurred instead of the configured address. The only known way to make it to
* go back to a valid state is having a valid buffer processing. Of course it
* requires at least a doubled buffer size to avoid an access to invalid memory
* region. But it does not fix everything. It may happen more than one
* consecutive SBL overflows. In that case, it might be unpredictable how many
* buffers the allocated memory should fit. For that case, a recover
* configuration was created. It produces the minimum buffer size for each H3A
* module and decrease the change for more SBL overflows. This recover state
* will be enabled every time a SBL overflow occur. As the output buffer size
* isn't big, it's possible to have an extra size able to fit many recover
* buffers making it extreamily unlikely to have an access to invalid memory
* region.
*/
#define NUM_H3A_RECOVER_BUFS 10
/*
* HACK: Because of HW issues the generic layer sometimes need to have
* different behaviour for different statistic modules.
*/
#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af)
#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb)
#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat))
static void __isp_stat_buf_sync_magic(struct ispstat *stat,
struct ispstat_buffer *buf,
u32 buf_size, enum dma_data_direction dir,
void (*dma_sync)(struct device *,
dma_addr_t, unsigned long, size_t,
enum dma_data_direction))
{
struct device *dev = stat->isp->dev;
struct page *pg;
dma_addr_t dma_addr;
u32 offset;
/* Initial magic words */
pg = vmalloc_to_page(buf->virt_addr);
dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
/* Final magic words */
pg = vmalloc_to_page(buf->virt_addr + buf_size);
dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
}
static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
struct ispstat_buffer *buf,
u32 buf_size,
enum dma_data_direction dir)
{
if (IS_COHERENT_BUF(stat))
return;
__isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
dma_sync_single_range_for_device);
}
static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
struct ispstat_buffer *buf,
u32 buf_size,
enum dma_data_direction dir)
{
if (IS_COHERENT_BUF(stat))
return;
__isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
dma_sync_single_range_for_cpu);
}
static int isp_stat_buf_check_magic(struct ispstat *stat,
struct ispstat_buffer *buf)
{
const u32 buf_size = IS_H3A_AF(stat) ?
buf->buf_size + AF_EXTRA_DATA : buf->buf_size;
u8 *w;
u8 *end;
int ret = -EINVAL;
isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
/* Checking initial magic numbers. They shouldn't be here anymore. */
for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++)
if (likely(*w != MAGIC_NUM))
ret = 0;
if (ret) {
dev_dbg(stat->isp->dev, "%s: beginning magic check does not "
"match.\n", stat->subdev.name);
return ret;
}
/* Checking magic numbers at the end. They must be still here. */
for (w = buf->virt_addr +