// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*/
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/timekeeping.h>
#include <linux/uaccess.h>
#include "isp.h"
#define ISP_STAT_USES_DMAENGINE(stat) ((stat)->dma_ch != NULL)
/*
* 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))
{
/* Sync the initial and final magic words. */
dma_sync(stat->isp->dev, buf->dma_addr, 0, MAGIC_SIZE, dir);
dma_sync(stat->isp->dev, buf->dma_addr + (buf_size & PAGE_MASK),
buf_size & ~PAGE_MASK, 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 (ISP_STAT_USES_DMAENGINE(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 (ISP_STAT_USES_DMAENGINE(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 + buf_size, end = w + MAGIC_SIZE;
w < end; w++) {
if (unlikely(*w != MAGIC_NUM)) {
dev_dbg(stat->isp->dev,
"%s: ending magic check does not match.\n",
stat->subdev.name);
return -EINVAL;
}
}
isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
DMA_FROM_DEVICE);
return 0;
}
static void isp_stat_buf_insert_magic(struct ispstat *stat,
struct ispstat_buffer *buf)
{
const u32 buf_size = IS_H3A_AF(stat) ?
stat->buf_size + AF_EXTRA_DATA : stat->buf_size;
isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
/*
* Inserting MAGIC_NUM at the beginning