Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/media/platform/st/sti/bdisp/Kconfig"
source "drivers/media/platform/st/sti/delta/Kconfig"
source "drivers/media/platform/st/sti/hva/Kconfig"
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += bdisp/
obj-y += delta/
obj-y += hva/
obj-y += stm32/
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_STI_BDISP
tristate "STMicroelectronics BDISP 2D blitter driver"
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_STI || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_STI_BDISP) += bdisp.o
bdisp-objs := bdisp-v4l2.o bdisp-hw.o bdisp-debug.o
@@ -0,0 +1,658 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
*/
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
#include "bdisp.h"
#include "bdisp-filter.h"
#include "bdisp-reg.h"
void bdisp_dbg_perf_begin(struct bdisp_dev *bdisp)
{
bdisp->dbg.hw_start = ktime_get();
}
void bdisp_dbg_perf_end(struct bdisp_dev *bdisp)
{
s64 time_us;
time_us = ktime_us_delta(ktime_get(), bdisp->dbg.hw_start);
if (!bdisp->dbg.min_duration)
bdisp->dbg.min_duration = time_us;
else
bdisp->dbg.min_duration = min(time_us, bdisp->dbg.min_duration);
bdisp->dbg.last_duration = time_us;
bdisp->dbg.max_duration = max(time_us, bdisp->dbg.max_duration);
bdisp->dbg.tot_duration += time_us;
}
static void bdisp_dbg_dump_ins(struct seq_file *s, u32 val)
{
seq_printf(s, "INS\t0x%08X\t", val);
switch (val & BLT_INS_S1_MASK) {
case BLT_INS_S1_OFF:
break;
case BLT_INS_S1_MEM:
seq_puts(s, "SRC1=mem - ");
break;
case BLT_INS_S1_CF:
seq_puts(s, "SRC1=ColorFill - ");
break;
case BLT_INS_S1_COPY:
seq_puts(s, "SRC1=copy - ");
break;
case BLT_INS_S1_FILL:
seq_puts(s, "SRC1=fil - ");
break;
default:
seq_puts(s, "SRC1=??? - ");
break;
}
switch (val & BLT_INS_S2_MASK) {
case BLT_INS_S2_OFF:
break;
case BLT_INS_S2_MEM:
seq_puts(s, "SRC2=mem - ");
break;
case BLT_INS_S2_CF:
seq_puts(s, "SRC2=ColorFill - ");
break;
default:
seq_puts(s, "SRC2=??? - ");
break;
}
if ((val & BLT_INS_S3_MASK) == BLT_INS_S3_MEM)
seq_puts(s, "SRC3=mem - ");
if (val & BLT_INS_IVMX)
seq_puts(s, "IVMX - ");
if (val & BLT_INS_CLUT)
seq_puts(s, "CLUT - ");
if (val & BLT_INS_SCALE)
seq_puts(s, "Scale - ");
if (val & BLT_INS_FLICK)
seq_puts(s, "Flicker - ");
if (val & BLT_INS_CLIP)
seq_puts(s, "Clip - ");
if (val & BLT_INS_CKEY)
seq_puts(s, "ColorKey - ");
if (val & BLT_INS_OVMX)
seq_puts(s, "OVMX - ");
if (val & BLT_INS_DEI)
seq_puts(s, "Deint - ");
if (val & BLT_INS_PMASK)
seq_puts(s, "PlaneMask - ");
if (val & BLT_INS_VC1R)
seq_puts(s, "VC1R - ");
if (val & BLT_INS_ROTATE)
seq_puts(s, "Rotate - ");
if (val & BLT_INS_GRAD)
seq_puts(s, "GradFill - ");
if (val & BLT_INS_AQLOCK)
seq_puts(s, "AQLock - ");
if (val & BLT_INS_PACE)
seq_puts(s, "Pace - ");
if (val & BLT_INS_IRQ)
seq_puts(s, "IRQ - ");
seq_putc(s, '\n');
}
static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val)
{
seq_printf(s, "TTY\t0x%08X\t", val);
seq_printf(s, "Pitch=%d - ", val & 0xFFFF);
switch ((val & BLT_TTY_COL_MASK) >> BLT_TTY_COL_SHIFT) {
case BDISP_RGB565:
seq_puts(s, "RGB565 - ");
break;
case BDISP_RGB888:
seq_puts(s, "RGB888 - ");
break;
case BDISP_XRGB8888:
seq_puts(s, "xRGB888 - ");
break;
case BDISP_ARGB8888:
seq_puts(s, "ARGB8888 - ");
break;
case BDISP_NV12:
seq_puts(s, "NV12 - ");
break;
case BDISP_YUV_3B:
seq_puts(s, "YUV420P - ");
break;
default:
seq_puts(s, "ColorFormat ??? - ");
break;
}
if (val & BLT_TTY_ALPHA_R)
seq_puts(s, "AlphaRange - ");
if (val & BLT_TTY_CR_NOT_CB)
seq_puts(s, "CrNotCb - ");
if (val & BLT_TTY_MB)
seq_puts(s, "MB - ");
if (val & BLT_TTY_HSO)
seq_puts(s, "HSO inverse - ");
if (val & BLT_TTY_VSO)
seq_puts(s, "VSO inverse - ");
if (val & BLT_TTY_DITHER)
seq_puts(s, "Dither - ");
if (val & BLT_TTY_CHROMA)
seq_puts(s, "Write CHROMA - ");
if (val & BLT_TTY_BIG_END)
seq_puts(s, "BigEndian - ");
seq_putc(s, '\n');
}
static void bdisp_dbg_dump_xy(struct seq_file *s, u32 val, char *name)
{
seq_printf(s, "%s\t0x%08X\t", name, val);
seq_printf(s, "(%d,%d)\n", val & 0xFFFF, (val >> 16));
}
static void bdisp_dbg_dump_sz(struct seq_file *s, u32 val, char *name)
{
seq_printf(s, "%s\t0x%08X\t", name, val);
seq_printf(s, "%dx%d\n", val & 0x1FFF, (val >> 16) & 0x1FFF);
}
static void bdisp_dbg_dump_sty(struct seq_file *s,
u32 val, u32 addr, char *name)
{
bool s1, s2, s3;
seq_printf(s, "%s\t0x%08X\t", name, val);
if (!addr || !name || (strlen(name) < 2))
goto done;
s1 = name[strlen(name) - 1] == '1';
s2 = name[strlen(name) - 1] == '2';
s3 = name[strlen(name) - 1] == '3';
seq_printf(s, "Pitch=%d - ", val & 0xFFFF);
switch ((val & BLT_TTY_COL_MASK) >> BLT_TTY_COL_SHIFT) {
case BDISP_RGB565:
seq_puts(s, "RGB565 - ");
break;
case BDISP_RGB888:
seq_puts(s, "RGB888 - ");
break;
case BDISP_XRGB8888:
seq_puts(s, "xRGB888 - ");
break;
case BDISP_ARGB8888:
seq_puts(s, "ARGB888 - ");
break;
case BDISP_NV12:
seq_puts(s, "NV12 - ");
break;
case BDISP_YUV_3B:
seq_puts(s, "YUV420P - ");
break;
default:
seq_puts(s, "ColorFormat ??? - ");
break;
}
if ((val & BLT_TTY_ALPHA_R) && !s3)
seq_puts(s, "AlphaRange - ");
if ((val & BLT_S1TY_A1_SUBSET) && !s3)
seq_puts(s, "A1SubSet - ");
if ((val & BLT_TTY_MB) && !s1)
seq_puts(s, "MB - ");
if (val & BLT_TTY_HSO)
seq_puts(s, "HSO inverse - ");
if (val & BLT_TTY_VSO)
seq_puts(s, "VSO inverse - ");
if ((val & BLT_S1TY_CHROMA_EXT) && (s1 || s2))
seq_puts(s, "ChromaExt - ");
if ((val & BLT_S3TY_BLANK_ACC) && s3)
seq_puts(s, "Blank Acc - ");
if ((val & BTL_S1TY_SUBBYTE) && !s3)
seq_puts(s, "SubByte - ");
if ((val & BLT_S1TY_RGB_EXP) && !s3)
seq_puts(s, "RGBExpand - ");
if ((val & BLT_TTY_BIG_END) && !s3)
seq_puts(s, "BigEndian - ");
done:
seq_putc(s, '\n');
}
static void bdisp_dbg_dump_fctl(struct seq_file *s, u32 val)
{
seq_printf(s, "FCTL\t0x%08X\t", val);
if ((val & BLT_FCTL_Y_HV_SCALE) == BLT_FCTL_Y_HV_SCALE)
seq_puts(s, "Resize Luma - ");
else if ((val & BLT_FCTL_Y_HV_SCALE) == BLT_FCTL_Y_HV_SAMPLE)
seq_puts(s, "Sample Luma - ");
if ((val & BLT_FCTL_HV_SCALE) == BLT_FCTL_HV_SCALE)
seq_puts(s, "Resize Chroma");
else if ((val & BLT_FCTL_HV_SCALE) == BLT_FCTL_HV_SAMPLE)
seq_puts(s, "Sample Chroma");
seq_putc(s, '\n');
}
static void bdisp_dbg_dump_rsf(struct seq_file *s, u32 val, char *name)
{
u32 inc;
seq_printf(s, "%s\t0x%08X\t", name, val);
if (!val)
goto done;
inc = val & 0xFFFF;
seq_printf(s, "H: %d(6.10) / scale~%dx0.1 - ", inc, 1024 * 10 / inc);
inc = val >> 16;
seq_printf(s, "V: %d(6.10) / scale~%dx0.1", inc, 1024 * 10 / inc);
done:
seq_putc(s, '\n');
}
static void bdisp_dbg_dump_rzi(struct seq_file *s, u32 val, char *name)
{
seq_printf(s, "%s\t0x%08X\t", name, val);
if (!val)
goto done;
seq_printf(s, "H: init=%d repeat=%d - ", val & 0x3FF, (val >> 12) & 7);
val >>= 16;
seq_printf(s, "V: init=%d repeat=%d", val & 0x3FF, (val >> 12) & 7);
done:
seq_putc(s, '\n');
}
static void bdisp_dbg_dump_ivmx(struct seq_file *s,
u32 c0, u32 c1, u32 c2, u32 c3)
{
seq_printf(s, "IVMX0\t0x%08X\n", c0);
seq_printf(s, "IVMX1\t0x%08X\n", c1);
seq_printf(s, "IVMX2\t0x%08X\n", c2);
seq_printf(s, "IVMX3\t0x%08X\t", c3);
if (!c0 && !c1 && !c2 && !c3) {
seq_putc(s, '\n');
return;
}
if ((c0 == bdisp_rgb_to_yuv[0]) &&
(c1 == bdisp_rgb_to_yuv[1]) &&
(c2 == bdisp_rgb_to_yuv[2]) &&
(c3 == bdisp_rgb_to_yuv[3])) {
seq_puts(s, "RGB to YUV\n");
return;
}
if ((c0 == bdisp_yuv_to_rgb[0]) &&
(c1 == bdisp_yuv_to_rgb[1]) &&
(c2 == bdisp_yuv_to_rgb[2]) &&
(c3 == bdisp_yuv_to_rgb[3])) {
seq_puts(s, "YUV to RGB\n");
return;
}
seq_puts(s, "Unknown conversion\n");
}
static int last_nodes_show(struct seq_file *s, void *data)
{
/* Not dumping all fields, focusing on significant ones */
struct bdisp_dev *bdisp = s->private;
struct bdisp_node *node;
int i = 0;
if (!bdisp->dbg.copy_node[0]) {
seq_puts(s, "No node built yet\n");
return 0;
}
do {
node = bdisp->dbg.copy_node[i];
if (!node)
break;
seq_printf(s, "--------\nNode %d:\n", i);
seq_puts(s, "-- General --\n");
seq_printf(s, "NIP\t0x%08X\n", node->nip);
seq_printf(s, "CIC\t0x%08X\n", node->cic);
bdisp_dbg_dump_ins(s, node->ins);
seq_printf(s, "ACK\t0x%08X\n", node->ack);
seq_puts(s, "-- Target --\n");
seq_printf(s, "TBA\t0x%08X\n", node->tba);
bdisp_dbg_dump_tty(s, node->tty);
bdisp_dbg_dump_xy(s, node->txy, "TXY");
bdisp_dbg_dump_sz(s, node->tsz, "TSZ");
/* Color Fill not dumped */
seq_puts(s, "-- Source 1 --\n");
seq_printf(s, "S1BA\t0x%08X\n", node->s1ba);
bdisp_dbg_dump_sty(s, node->s1ty, node->s1ba, "S1TY");
bdisp_dbg_dump_xy(s, node->s1xy, "S1XY");
seq_puts(s, "-- Source 2 --\n");
seq_printf(s, "S2BA\t0x%08X\n", node->s2ba);
bdisp_dbg_dump_sty(s, node->s2ty, node->s2ba, "S2TY");
bdisp_dbg_dump_xy(s, node->s2xy, "S2XY");
bdisp_dbg_dump_sz(s, node->s2sz, "S2SZ");
seq_puts(s, "-- Source 3 --\n");
seq_printf(s, "S3BA\t0x%08X\n", node->s3ba);
bdisp_dbg_dump_sty(s, node->s3ty, node->s3ba, "S3TY");
bdisp_dbg_dump_xy(s, node->s3xy, "S3XY");
bdisp_dbg_dump_sz(s, node->s3sz, "S3SZ");
/* Clipping not dumped */
/* CLUT not dumped */
seq_puts(s, "-- Filter & Mask --\n");
bdisp_dbg_dump_fctl(s, node->fctl);
/* PMK not dumped */
seq_puts(s, "-- Chroma Filter --\n");
bdisp_dbg_dump_rsf(s, node->rsf, "RSF");
bdisp_dbg_dump_rzi(s, node->rzi, "RZI");
seq_printf(s, "HFP\t0x%08X\n", node->hfp);
seq_printf(s, "VFP\t0x%08X\n", node->vfp);
seq_puts(s, "-- Luma Filter --\n");
bdisp_dbg_dump_rsf(s, node->y_rsf, "Y_RSF");
bdisp_dbg_dump_rzi(s, node->y_rzi, "Y_RZI");
seq_printf(s, "Y_HFP\t0x%08X\n", node->y_hfp);
seq_printf(s, "Y_VFP\t0x%08X\n", node->y_vfp);
/* Flicker not dumped */
/* Color key not dumped */
/* Reserved not dumped */
/* Static Address & User not dumped */
seq_puts(s, "-- Input Versatile Matrix --\n");
bdisp_dbg_dump_ivmx(s, node->ivmx0, node->ivmx1,
node->ivmx2, node->ivmx3);
/* Output Versatile Matrix not dumped */
/* Pace not dumped */
/* VC1R & DEI not dumped */
/* Gradient Fill not dumped */
} while ((++i < MAX_NB_NODE) && node->nip);
return 0;
}
static int last_nodes_raw_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
struct bdisp_node *node;
u32 *val;
int j, i = 0;
if (!bdisp->dbg.copy_node[0]) {
seq_puts(s, "No node built yet\n");
return 0;
}
do {
node = bdisp->dbg.copy_node[i];
if (!node)
break;
seq_printf(s, "--------\nNode %d:\n", i);
val = (u32 *)node;
for (j = 0; j < sizeof(struct bdisp_node) / sizeof(u32); j++)
seq_printf(s, "0x%08X\n", *val++);
} while ((++i < MAX_NB_NODE) && node->nip);
return 0;
}
static const char *bdisp_fmt_to_str(struct bdisp_frame frame)
{
switch (frame.fmt->pixelformat) {
case V4L2_PIX_FMT_YUV420:
return "YUV420P";
case V4L2_PIX_FMT_NV12:
if (frame.field == V4L2_FIELD_INTERLACED)
return "NV12 interlaced";
else
return "NV12";
case V4L2_PIX_FMT_RGB565:
return "RGB16";
case V4L2_PIX_FMT_RGB24:
return "RGB24";
case V4L2_PIX_FMT_XBGR32:
return "XRGB";
case V4L2_PIX_FMT_ABGR32:
return "ARGB";
default:
return "????";
}
}
static int last_request_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
struct bdisp_request *request = &bdisp->dbg.copy_request;
struct bdisp_frame src, dst;
if (!request->nb_req) {
seq_puts(s, "No request\n");
return 0;
}
src = request->src;
dst = request->dst;
seq_printf(s, "\nRequest #%d\n", request->nb_req);
seq_printf(s, "Format: %s\t\t\t%s\n",
bdisp_fmt_to_str(src), bdisp_fmt_to_str(dst));
seq_printf(s, "Crop area: (%d,%d)/%ux%u ==>\t(%d,%d)/%ux%u\n",
src.crop.left, src.crop.top,
src.crop.width, src.crop.height,
dst.crop.left, dst.crop.top,
dst.crop.width, dst.crop.height);
seq_printf(s, "Buff size: %dx%d\t\t%dx%d\n\n",
src.width, src.height, dst.width, dst.height);
if (request->hflip)
seq_puts(s, "Horizontal flip\n\n");
if (request->vflip)
seq_puts(s, "Vertical flip\n\n");
return 0;
}
#define DUMP(reg) seq_printf(s, #reg " \t0x%08X\n", readl(bdisp->regs + reg))
static int regs_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
int ret;
unsigned int i;
ret = pm_runtime_resume_and_get(bdisp->dev);
if (ret < 0) {
seq_puts(s, "Cannot wake up IP\n");
return 0;
}
seq_printf(s, "Reg @ = 0x%p\n", bdisp->regs);
seq_puts(s, "\nStatic:\n");
DUMP(BLT_CTL);
DUMP(BLT_ITS);
DUMP(BLT_STA1);
DUMP(BLT_AQ1_CTL);
DUMP(BLT_AQ1_IP);
DUMP(BLT_AQ1_LNA);
DUMP(BLT_AQ1_STA);
DUMP(BLT_ITM0);
seq_puts(s, "\nPlugs:\n");
DUMP(BLT_PLUGS1_OP2);
DUMP(BLT_PLUGS1_CHZ);
DUMP(BLT_PLUGS1_MSZ);
DUMP(BLT_PLUGS1_PGZ);
DUMP(BLT_PLUGS2_OP2);
DUMP(BLT_PLUGS2_CHZ);
DUMP(BLT_PLUGS2_MSZ);
DUMP(BLT_PLUGS2_PGZ);
DUMP(BLT_PLUGS3_OP2);
DUMP(BLT_PLUGS3_CHZ);
DUMP(BLT_PLUGS3_MSZ);
DUMP(BLT_PLUGS3_PGZ);
DUMP(BLT_PLUGT_OP2);
DUMP(BLT_PLUGT_CHZ);
DUMP(BLT_PLUGT_MSZ);
DUMP(BLT_PLUGT_PGZ);
seq_puts(s, "\nNode:\n");
DUMP(BLT_NIP);
DUMP(BLT_CIC);
DUMP(BLT_INS);
DUMP(BLT_ACK);
DUMP(BLT_TBA);
DUMP(BLT_TTY);
DUMP(BLT_TXY);
DUMP(BLT_TSZ);
DUMP(BLT_S1BA);
DUMP(BLT_S1TY);
DUMP(BLT_S1XY);
DUMP(BLT_S2BA);
DUMP(BLT_S2TY);
DUMP(BLT_S2XY);
DUMP(BLT_S2SZ);
DUMP(BLT_S3BA);
DUMP(BLT_S3TY);
DUMP(BLT_S3XY);
DUMP(BLT_S3SZ);
DUMP(BLT_FCTL);
DUMP(BLT_RSF);
DUMP(BLT_RZI);
DUMP(BLT_HFP);
DUMP(BLT_VFP);
DUMP(BLT_Y_RSF);
DUMP(BLT_Y_RZI);
DUMP(BLT_Y_HFP);
DUMP(BLT_Y_VFP);
DUMP(BLT_IVMX0);
DUMP(BLT_IVMX1);
DUMP(BLT_IVMX2);
DUMP(BLT_IVMX3);
DUMP(BLT_OVMX0);
DUMP(BLT_OVMX1);
DUMP(BLT_OVMX2);
DUMP(BLT_OVMX3);
DUMP(BLT_DEI);
seq_puts(s, "\nFilter:\n");
for (i = 0; i < BLT_NB_H_COEF; i++) {
seq_printf(s, "BLT_HFC%d \t0x%08X\n", i,
readl(bdisp->regs + BLT_HFC_N + i * 4));
}
for (i = 0; i < BLT_NB_V_COEF; i++) {
seq_printf(s, "BLT_VFC%d \t0x%08X\n", i,
readl(bdisp->regs + BLT_VFC_N + i * 4));
}
seq_puts(s, "\nLuma filter:\n");
for (i = 0; i < BLT_NB_H_COEF; i++) {
seq_printf(s, "BLT_Y_HFC%d \t0x%08X\n", i,
readl(bdisp->regs + BLT_Y_HFC_N + i * 4));
}
for (i = 0; i < BLT_NB_V_COEF; i++) {
seq_printf(s, "BLT_Y_VFC%d \t0x%08X\n", i,
readl(bdisp->regs + BLT_Y_VFC_N + i * 4));
}
pm_runtime_put(bdisp->dev);
return 0;
}
#define SECOND 1000000
static int perf_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
struct bdisp_request *request = &bdisp->dbg.copy_request;
s64 avg_time_us;
int avg_fps, min_fps, max_fps, last_fps;
if (!request->nb_req) {
seq_puts(s, "No request\n");
return 0;
}
avg_time_us = div64_s64(bdisp->dbg.tot_duration, request->nb_req);
if (avg_time_us > SECOND)
avg_fps = 0;
else
avg_fps = SECOND / (s32)avg_time_us;
if (bdisp->dbg.min_duration > SECOND)
min_fps = 0;
else
min_fps = SECOND / (s32)bdisp->dbg.min_duration;
if (bdisp->dbg.max_duration > SECOND)
max_fps = 0;
else
max_fps = SECOND / (s32)bdisp->dbg.max_duration;
if (bdisp->dbg.last_duration > SECOND)
last_fps = 0;
else
last_fps = SECOND / (s32)bdisp->dbg.last_duration;
seq_printf(s, "HW processing (%d requests):\n", request->nb_req);
seq_printf(s, " Average: %5lld us (%3d fps)\n",
avg_time_us, avg_fps);
seq_printf(s, " Min-Max: %5lld us (%3d fps) - %5lld us (%3d fps)\n",
bdisp->dbg.min_duration, min_fps,
bdisp->dbg.max_duration, max_fps);
seq_printf(s, " Last: %5lld us (%3d fps)\n",
bdisp->dbg.last_duration, last_fps);
return 0;
}
#define bdisp_dbg_create_entry(name) \
debugfs_create_file(#name, S_IRUGO, bdisp->dbg.debugfs_entry, bdisp, \
&name##_fops)
DEFINE_SHOW_ATTRIBUTE(regs);
DEFINE_SHOW_ATTRIBUTE(last_nodes);
DEFINE_SHOW_ATTRIBUTE(last_nodes_raw);
DEFINE_SHOW_ATTRIBUTE(last_request);
DEFINE_SHOW_ATTRIBUTE(perf);
void bdisp_debugfs_create(struct bdisp_dev *bdisp)
{
char dirname[16];
snprintf(dirname, sizeof(dirname), "%s%d", BDISP_NAME, bdisp->id);
bdisp->dbg.debugfs_entry = debugfs_create_dir(dirname, NULL);
bdisp_dbg_create_entry(regs);
bdisp_dbg_create_entry(last_nodes);
bdisp_dbg_create_entry(last_nodes_raw);
bdisp_dbg_create_entry(last_request);
bdisp_dbg_create_entry(perf);
}
void bdisp_debugfs_remove(struct bdisp_dev *bdisp)
{
debugfs_remove_recursive(bdisp->dbg.debugfs_entry);
bdisp->dbg.debugfs_entry = NULL;
}
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
*/
#define BDISP_HF_NB 64
#define BDISP_VF_NB 40
/**
* struct bdisp_filter_h_spec - Horizontal filter specification
*
* @min: min scale factor for this filter (6.10 fixed point)
* @max: max scale factor for this filter (6.10 fixed point)
* @coef: filter coefficients
*/
struct bdisp_filter_h_spec {
const u16 min;
const u16 max;
const u8 coef[BDISP_HF_NB];
};
/**
* struct bdisp_filter_v_spec - Vertical filter specification
*
* @min: min scale factor for this filter (6.10 fixed point)
* @max: max scale factor for this filter (6.10 fixed point)
* @coef: filter coefficients
*/
struct bdisp_filter_v_spec {
const u16 min;
const u16 max;
const u8 coef[BDISP_VF_NB];
};
/* RGB YUV 601 standard conversion */
static const u32 bdisp_rgb_to_yuv[] = {
0x0e1e8bee, 0x08420419, 0xfb5ed471, 0x08004080,
};
static const u32 bdisp_yuv_to_rgb[] = {
0x3324a800, 0xe604ab9c, 0x0004a957, 0x32121eeb,
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,235 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
*/
struct bdisp_node {
/* 0 - General */
u32 nip;
u32 cic;
u32 ins;
u32 ack;
/* 1 - Target */
u32 tba;
u32 tty;
u32 txy;
u32 tsz;
/* 2 - Color Fill */
u32 s1cf;
u32 s2cf;
/* 3 - Source 1 */
u32 s1ba;
u32 s1ty;
u32 s1xy;
u32 s1sz_tsz;
/* 4 - Source 2 */
u32 s2ba;
u32 s2ty;
u32 s2xy;
u32 s2sz;
/* 5 - Source 3 */
u32 s3ba;
u32 s3ty;
u32 s3xy;
u32 s3sz;
/* 6 - Clipping */
u32 cwo;
u32 cws;
/* 7 - CLUT */
u32 cco;
u32 cml;
/* 8 - Filter & Mask */
u32 fctl;
u32 pmk;
/* 9 - Chroma Filter */
u32 rsf;
u32 rzi;
u32 hfp;
u32 vfp;
/* 10 - Luma Filter */
u32 y_rsf;
u32 y_rzi;
u32 y_hfp;
u32 y_vfp;
/* 11 - Flicker */
u32 ff0;
u32 ff1;
u32 ff2;
u32 ff3;
/* 12 - Color Key */
u32 key1;
u32 key2;
/* 14 - Static Address & User */
u32 sar;
u32 usr;
/* 15 - Input Versatile Matrix */
u32 ivmx0;
u32 ivmx1;
u32 ivmx2;
u32 ivmx3;
/* 16 - Output Versatile Matrix */
u32 ovmx0;
u32 ovmx1;
u32 ovmx2;
u32 ovmx3;
/* 17 - Pace */
u32 pace;
/* 18 - VC1R & DEI */
u32 vc1r;
u32 dei;
/* 19 - Gradient Fill */
u32 hgf;
u32 vgf;
};
/* HW registers : static */
#define BLT_CTL 0x0A00
#define BLT_ITS 0x0A04
#define BLT_STA1 0x0A08
#define BLT_AQ1_CTL 0x0A60
#define BLT_AQ1_IP 0x0A64
#define BLT_AQ1_LNA 0x0A68
#define BLT_AQ1_STA 0x0A6C
#define BLT_ITM0 0x0AD0
/* HW registers : plugs */
#define BLT_PLUGS1_OP2 0x0B04
#define BLT_PLUGS1_CHZ 0x0B08
#define BLT_PLUGS1_MSZ 0x0B0C
#define BLT_PLUGS1_PGZ 0x0B10
#define BLT_PLUGS2_OP2 0x0B24
#define BLT_PLUGS2_CHZ 0x0B28
#define BLT_PLUGS2_MSZ 0x0B2C
#define BLT_PLUGS2_PGZ 0x0B30
#define BLT_PLUGS3_OP2 0x0B44
#define BLT_PLUGS3_CHZ 0x0B48
#define BLT_PLUGS3_MSZ 0x0B4C
#define BLT_PLUGS3_PGZ 0x0B50
#define BLT_PLUGT_OP2 0x0B84
#define BLT_PLUGT_CHZ 0x0B88
#define BLT_PLUGT_MSZ 0x0B8C
#define BLT_PLUGT_PGZ 0x0B90
/* HW registers : node */
#define BLT_NIP 0x0C00
#define BLT_CIC 0x0C04
#define BLT_INS 0x0C08
#define BLT_ACK 0x0C0C
#define BLT_TBA 0x0C10
#define BLT_TTY 0x0C14
#define BLT_TXY 0x0C18
#define BLT_TSZ 0x0C1C
#define BLT_S1BA 0x0C28
#define BLT_S1TY 0x0C2C
#define BLT_S1XY 0x0C30
#define BLT_S2BA 0x0C38
#define BLT_S2TY 0x0C3C
#define BLT_S2XY 0x0C40
#define BLT_S2SZ 0x0C44
#define BLT_S3BA 0x0C48
#define BLT_S3TY 0x0C4C
#define BLT_S3XY 0x0C50
#define BLT_S3SZ 0x0C54
#define BLT_FCTL 0x0C68
#define BLT_RSF 0x0C70
#define BLT_RZI 0x0C74
#define BLT_HFP 0x0C78
#define BLT_VFP 0x0C7C
#define BLT_Y_RSF 0x0C80
#define BLT_Y_RZI 0x0C84
#define BLT_Y_HFP 0x0C88
#define BLT_Y_VFP 0x0C8C
#define BLT_IVMX0 0x0CC0
#define BLT_IVMX1 0x0CC4
#define BLT_IVMX2 0x0CC8
#define BLT_IVMX3 0x0CCC
#define BLT_OVMX0 0x0CD0
#define BLT_OVMX1 0x0CD4
#define BLT_OVMX2 0x0CD8
#define BLT_OVMX3 0x0CDC
#define BLT_DEI 0x0CEC
/* HW registers : filters */
#define BLT_HFC_N 0x0D00
#define BLT_VFC_N 0x0D90
#define BLT_Y_HFC_N 0x0E00
#define BLT_Y_VFC_N 0x0E90
#define BLT_NB_H_COEF 16
#define BLT_NB_V_COEF 10
/* Registers values */
#define BLT_CTL_RESET BIT(31) /* Global soft reset */
#define BLT_ITS_AQ1_LNA BIT(12) /* AQ1 LNA reached */
#define BLT_STA1_IDLE BIT(0) /* BDISP idle */
#define BLT_AQ1_CTL_CFG 0x80400003 /* Enable, P3, LNA reached */
#define BLT_INS_S1_MASK (BIT(0) | BIT(1) | BIT(2))
#define BLT_INS_S1_OFF 0x00000000 /* src1 disabled */
#define BLT_INS_S1_MEM 0x00000001 /* src1 fetched from memory */
#define BLT_INS_S1_CF 0x00000003 /* src1 color fill */
#define BLT_INS_S1_COPY 0x00000004 /* src1 direct copy */
#define BLT_INS_S1_FILL 0x00000007 /* src1 firect fill */
#define BLT_INS_S2_MASK (BIT(3) | BIT(4))
#define BLT_INS_S2_OFF 0x00000000 /* src2 disabled */
#define BLT_INS_S2_MEM 0x00000008 /* src2 fetched from memory */
#define BLT_INS_S2_CF 0x00000018 /* src2 color fill */
#define BLT_INS_S3_MASK BIT(5)
#define BLT_INS_S3_OFF 0x00000000 /* src3 disabled */
#define BLT_INS_S3_MEM 0x00000020 /* src3 fetched from memory */
#define BLT_INS_IVMX BIT(6) /* Input versatile matrix */
#define BLT_INS_CLUT BIT(7) /* Color Look Up Table */
#define BLT_INS_SCALE BIT(8) /* Scaling */
#define BLT_INS_FLICK BIT(9) /* Flicker filter */
#define BLT_INS_CLIP BIT(10) /* Clipping */
#define BLT_INS_CKEY BIT(11) /* Color key */
#define BLT_INS_OVMX BIT(12) /* Output versatile matrix */
#define BLT_INS_DEI BIT(13) /* Deinterlace */
#define BLT_INS_PMASK BIT(14) /* Plane mask */
#define BLT_INS_VC1R BIT(17) /* VC1 Range mapping */
#define BLT_INS_ROTATE BIT(18) /* Rotation */
#define BLT_INS_GRAD BIT(19) /* Gradient fill */
#define BLT_INS_AQLOCK BIT(29) /* AQ lock */
#define BLT_INS_PACE BIT(30) /* Pace down */
#define BLT_INS_IRQ BIT(31) /* Raise IRQ when node done */
#define BLT_CIC_ALL_GRP 0x000FDFFC /* all valid groups present */
#define BLT_ACK_BYPASS_S2S3 0x00000007 /* Bypass src2 and src3 */
#define BLT_TTY_COL_SHIFT 16 /* Color format */
#define BLT_TTY_COL_MASK 0x001F0000 /* Color format mask */
#define BLT_TTY_ALPHA_R BIT(21) /* Alpha range */
#define BLT_TTY_CR_NOT_CB BIT(22) /* CR not Cb */
#define BLT_TTY_MB BIT(23) /* MB frame / field*/
#define BLT_TTY_HSO BIT(24) /* H scan order */
#define BLT_TTY_VSO BIT(25) /* V scan order */
#define BLT_TTY_DITHER BIT(26) /* Dithering */
#define BLT_TTY_CHROMA BIT(27) /* Write chroma / luma */
#define BLT_TTY_BIG_END BIT(30) /* Big endianness */
#define BLT_S1TY_A1_SUBSET BIT(22) /* A1 subset */
#define BLT_S1TY_CHROMA_EXT BIT(26) /* Chroma Extended */
#define BTL_S1TY_SUBBYTE BIT(28) /* Sub-byte fmt, pixel order */
#define BLT_S1TY_RGB_EXP BIT(29) /* RGB expansion mode */
#define BLT_S2TY_A1_SUBSET BIT(22) /* A1 subset */
#define BLT_S2TY_CHROMA_EXT BIT(26) /* Chroma Extended */
#define BTL_S2TY_SUBBYTE BIT(28) /* Sub-byte fmt, pixel order */
#define BLT_S2TY_RGB_EXP BIT(29) /* RGB expansion mode */
#define BLT_S3TY_BLANK_ACC BIT(26) /* Blank access */
#define BLT_FCTL_HV_SCALE 0x00000055 /* H/V resize + color filter */
#define BLT_FCTL_Y_HV_SCALE 0x33000000 /* Luma version */
#define BLT_FCTL_HV_SAMPLE 0x00000044 /* H/V resize */
#define BLT_FCTL_Y_HV_SAMPLE 0x22000000 /* Luma version */
#define BLT_RZI_DEFAULT 0x20003000 /* H/VNB_repeat = 3/2 */
/* Color format */
#define BDISP_RGB565 0x00 /* RGB565 */
#define BDISP_RGB888 0x01 /* RGB888 */
#define BDISP_XRGB8888 0x02 /* RGB888_32 */
#define BDISP_ARGB8888 0x05 /* ARGB888 */
#define BDISP_NV12 0x16 /* YCbCr42x R2B */
#define BDISP_YUV_3B 0x1E /* YUV (3 buffer) */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
*/
#include <linux/clk.h>
#include <linux/ktime.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
#define BDISP_NAME "bdisp"
/*
* Max nb of nodes in node-list:
* - 2 nodes to handle wide 4K pictures
* - 2 nodes to handle two planes (Y & CbCr) */
#define MAX_OUTPUT_PLANES 2
#define MAX_VERTICAL_STRIDES 2
#define MAX_NB_NODE (MAX_OUTPUT_PLANES * MAX_VERTICAL_STRIDES)
/* struct bdisp_ctrls - bdisp control set
* @hflip: horizontal flip
* @vflip: vertical flip
*/
struct bdisp_ctrls {
struct v4l2_ctrl *hflip;
struct v4l2_ctrl *vflip;
};
/**
* struct bdisp_fmt - driver's internal color format data
* @pixelformat:fourcc code for this format
* @nb_planes: number of planes (ex: [0]=RGB/Y - [1]=Cb/Cr, ...)
* @bpp: bits per pixel (general)
* @bpp_plane0: byte per pixel for the 1st plane
* @w_align: width alignment in pixel (multiple of)
* @h_align: height alignment in pixel (multiple of)
*/
struct bdisp_fmt {
u32 pixelformat;
u8 nb_planes;
u8 bpp;
u8 bpp_plane0;
u8 w_align;
u8 h_align;
};
/**
* struct bdisp_frame - frame properties
*
* @width: frame width (including padding)
* @height: frame height (including padding)
* @fmt: pointer to frame format descriptor
* @field: frame / field type
* @bytesperline: stride of the 1st plane
* @sizeimage: image size in bytes
* @colorspace: colorspace
* @crop: crop area
* @paddr: image physical addresses per plane ([0]=RGB/Y - [1]=Cb/Cr, ...)
*/
struct bdisp_frame {
u32 width;
u32 height;
const struct bdisp_fmt *fmt;
enum v4l2_field field;
u32 bytesperline;
u32 sizeimage;
enum v4l2_colorspace colorspace;
struct v4l2_rect crop;
dma_addr_t paddr[4];
};
/**
* struct bdisp_request - bdisp request
*
* @src: source frame properties
* @dst: destination frame properties
* @hflip: horizontal flip
* @vflip: vertical flip
* @nb_req: number of run request
*/
struct bdisp_request {
struct bdisp_frame src;
struct bdisp_frame dst;
unsigned int hflip:1;
unsigned int vflip:1;
int nb_req;
};
/**
* struct bdisp_ctx - device context data
*
* @src: source frame properties
* @dst: destination frame properties
* @state: flags to keep track of user configuration
* @hflip: horizontal flip
* @vflip: vertical flip
* @bdisp_dev: the device this context applies to
* @node: node array
* @node_paddr: node physical address array
* @fh: v4l2 file handle
* @ctrl_handler: v4l2 controls handler
* @bdisp_ctrls: bdisp control set
* @ctrls_rdy: true if the control handler is initialized
*/
struct bdisp_ctx {
struct bdisp_frame src;
struct bdisp_frame dst;
u32 state;
unsigned int hflip:1;
unsigned int vflip:1;
struct bdisp_dev *bdisp_dev;
struct bdisp_node *node[MAX_NB_NODE];
dma_addr_t node_paddr[MAX_NB_NODE];
struct v4l2_fh fh;
struct v4l2_ctrl_handler ctrl_handler;
struct bdisp_ctrls bdisp_ctrls;
bool ctrls_rdy;
};
/**
* struct bdisp_m2m_device - v4l2 memory-to-memory device data
*
* @vdev: video device node for v4l2 m2m mode
* @m2m_dev: v4l2 m2m device data
* @ctx: hardware context data
* @refcnt: reference counter
*/
struct bdisp_m2m_device {
struct video_device *vdev;
struct v4l2_m2m_dev *m2m_dev;
struct bdisp_ctx *ctx;
int refcnt;
};
/**
* struct bdisp_dbg - debug info
*
* @debugfs_entry: debugfs
* @copy_node: array of last used nodes
* @copy_request: last bdisp request
* @hw_start: start time of last HW request
* @last_duration: last HW processing duration in microsecs
* @min_duration: min HW processing duration in microsecs
* @max_duration: max HW processing duration in microsecs
* @tot_duration: total HW processing duration in microsecs
*/
struct bdisp_dbg {
struct dentry *debugfs_entry;
struct bdisp_node *copy_node[MAX_NB_NODE];
struct bdisp_request copy_request;
ktime_t hw_start;
s64 last_duration;
s64 min_duration;
s64 max_duration;
s64 tot_duration;
};
/**
* struct bdisp_dev - abstraction for bdisp entity
*
* @v4l2_dev: v4l2 device
* @vdev: video device
* @pdev: platform device
* @dev: device
* @lock: mutex protecting this data structure
* @slock: spinlock protecting this data structure
* @id: device index
* @m2m: memory-to-memory V4L2 device information
* @state: flags used to synchronize m2m and capture mode operation
* @clock: IP clock
* @regs: registers
* @irq_queue: interrupt handler waitqueue
* @work_queue: workqueue to handle timeouts
* @timeout_work: IRQ timeout structure
* @dbg: debug info
*/
struct bdisp_dev {
struct v4l2_device v4l2_dev;
struct video_device vdev;
struct platform_device *pdev;
struct device *dev;
spinlock_t slock;
struct mutex lock;
u16 id;
struct bdisp_m2m_device m2m;
unsigned long state;
struct clk *clock;
void __iomem *regs;
wait_queue_head_t irq_queue;
struct workqueue_struct *work_queue;
struct delayed_work timeout_work;
struct bdisp_dbg dbg;
};
void bdisp_hw_free_nodes(struct bdisp_ctx *ctx);
int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx);
void bdisp_hw_free_filters(struct device *dev);
int bdisp_hw_alloc_filters(struct device *dev);
int bdisp_hw_reset(struct bdisp_dev *bdisp);
int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp);
int bdisp_hw_update(struct bdisp_ctx *ctx);
void bdisp_debugfs_remove(struct bdisp_dev *bdisp);
void bdisp_debugfs_create(struct bdisp_dev *bdisp);
void bdisp_dbg_perf_begin(struct bdisp_dev *bdisp);
void bdisp_dbg_perf_end(struct bdisp_dev *bdisp);
@@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_STI_DELTA
tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver"
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_STI || COMPILE_TEST
help
This V4L2 driver enables DELTA multi-format video decoder
of STMicroelectronics STiH4xx SoC series allowing hardware
decoding of various compressed video bitstream format in
raw uncompressed format.
Use this option to see the decoders available for such
hardware.
Please notice that the driver will only be built if
at least one of the DELTA decoder below is selected.
config VIDEO_STI_DELTA_MJPEG
bool "STMicroelectronics DELTA MJPEG support"
default y
depends on VIDEO_STI_DELTA
help
Enables DELTA MJPEG hardware support.
To compile this driver as a module, choose M here:
the module will be called st-delta.
config VIDEO_STI_DELTA_DRIVER
tristate
depends on VIDEO_STI_DELTA
depends on VIDEO_STI_DELTA_MJPEG
default VIDEO_STI_DELTA_MJPEG
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select RPMSG
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) += st-delta.o
st-delta-y := delta-v4l2.o delta-mem.o delta-ipc.o delta-debug.o
# MJPEG support
st-delta-$(CONFIG_VIDEO_STI_DELTA_MJPEG) += delta-mjpeg-hdr.o
st-delta-$(CONFIG_VIDEO_STI_DELTA_MJPEG) += delta-mjpeg-dec.o
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#ifndef DELTA_CFG_H
#define DELTA_CFG_H
#define DELTA_FW_VERSION "21.1-3"
#define DELTA_MIN_WIDTH 32
#define DELTA_MAX_WIDTH 4096
#define DELTA_MIN_HEIGHT 32
#define DELTA_MAX_HEIGHT 2400
/* DELTA requires a 32x32 pixels alignment for frames */
#define DELTA_WIDTH_ALIGNMENT 32
#define DELTA_HEIGHT_ALIGNMENT 32
#define DELTA_DEFAULT_WIDTH DELTA_MIN_WIDTH
#define DELTA_DEFAULT_HEIGHT DELTA_MIN_HEIGHT
#define DELTA_DEFAULT_FRAMEFORMAT V4L2_PIX_FMT_NV12
#define DELTA_DEFAULT_STREAMFORMAT V4L2_PIX_FMT_MJPEG
#define DELTA_MAX_RESO (DELTA_MAX_WIDTH * DELTA_MAX_HEIGHT)
/* guard value for number of access units */
#define DELTA_MAX_AUS 10
/* IP perf dependent, can be tuned */
#define DELTA_PEAK_FRAME_SMOOTHING 2
/*
* guard output frame count:
* - at least 1 frame needed for display
* - at worst 21
* ( max h264 dpb (16) +
* decoding peak smoothing (2) +
* user display pipeline (3) )
*/
#define DELTA_MIN_FRAME_USER 1
#define DELTA_MAX_DPB 16
#define DELTA_MAX_FRAME_USER 3 /* platform/use-case dependent */
#define DELTA_MAX_FRAMES (DELTA_MAX_DPB + DELTA_PEAK_FRAME_SMOOTHING +\
DELTA_MAX_FRAME_USER)
#if DELTA_MAX_FRAMES > VIDEO_MAX_FRAME
#undef DELTA_MAX_FRAMES
#define DELTA_MAX_FRAMES (VIDEO_MAX_FRAME)
#endif
/* extra space to be allocated to store codec specific data per frame */
#define DELTA_MAX_FRAME_PRIV_SIZE 100
/* PM runtime auto power-off after 5ms of inactivity */
#define DELTA_HW_AUTOSUSPEND_DELAY_MS 5
#define DELTA_MAX_DECODERS 10
#ifdef CONFIG_VIDEO_STI_DELTA_MJPEG
extern const struct delta_dec mjpegdec;
#endif
#endif /* DELTA_CFG_H */
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Hugues Fruchet <hugues.fruchet@st.com>
* Fabrice Lecoultre <fabrice.lecoultre@st.com>
* for STMicroelectronics.
*/
#include "delta.h"
#include "delta-debug.h"
char *delta_streaminfo_str(struct delta_streaminfo *s, char *str,
unsigned int len)
{
if (!s)
return NULL;
snprintf(str, len,
"%4.4s %dx%d %s %s dpb=%d %s %s %s(%d,%d)/%ux%u %s%d/%d",
(char *)&s->streamformat, s->width, s->height,
s->profile, s->level, s->dpb,
(s->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced",
s->other,
s->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "",
s->crop.left, s->crop.top,
s->crop.width, s->crop.height,
s->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "",
s->pixelaspect.numerator,
s->pixelaspect.denominator);
return str;
}
char *delta_frameinfo_str(struct delta_frameinfo *f, char *str,
unsigned int len)
{
if (!f)
return NULL;
snprintf(str, len,
"%4.4s %dx%d aligned %dx%d %s %s(%d,%d)/%ux%u %s%d/%d",
(char *)&f->pixelformat, f->width, f->height,
f->aligned_width, f->aligned_height,
(f->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced",
f->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "",
f->crop.left, f->crop.top,
f->crop.width, f->crop.height,
f->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "",
f->pixelaspect.numerator,
f->pixelaspect.denominator);
return str;
}
void delta_trace_summary(struct delta_ctx *ctx)
{
struct delta_dev *delta = ctx->dev;
struct delta_streaminfo *s = &ctx->streaminfo;
unsigned char str[100] = "";
if (!(ctx->flags & DELTA_FLAG_STREAMINFO))
return;
dev_dbg(delta->dev, "%s %s, %d frames decoded, %d frames output, %d frames dropped, %d stream errors, %d decode errors",
ctx->name,
delta_streaminfo_str(s, str, sizeof(str)),
ctx->decoded_frames,
ctx->output_frames,
ctx->dropped_frames,
ctx->stream_errors,
ctx->decode_errors);
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Hugues Fruchet <hugues.fruchet@st.com>
* Fabrice Lecoultre <fabrice.lecoultre@st.com>
* for STMicroelectronics.
*/
#ifndef DELTA_DEBUG_H
#define DELTA_DEBUG_H
char *delta_streaminfo_str(struct delta_streaminfo *s, char *str,
unsigned int len);
char *delta_frameinfo_str(struct delta_frameinfo *f, char *str,
unsigned int len);
void delta_trace_summary(struct delta_ctx *ctx);
#endif /* DELTA_DEBUG_H */
@@ -0,0 +1,591 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#include <linux/rpmsg.h>
#include "delta.h"
#include "delta-ipc.h"
#include "delta-mem.h"
#define IPC_TIMEOUT 100
#define IPC_SANITY_TAG 0xDEADBEEF
enum delta_ipc_fw_command {
DELTA_IPC_OPEN,
DELTA_IPC_SET_STREAM,
DELTA_IPC_DECODE,
DELTA_IPC_CLOSE
};
#define to_rpmsg_driver(__drv) container_of(__drv, struct rpmsg_driver, drv)
#define to_delta(__d) container_of(__d, struct delta_dev, rpmsg_driver)
#define to_ctx(hdl) ((struct delta_ipc_ctx *)hdl)
#define to_pctx(ctx) container_of(ctx, struct delta_ctx, ipc_ctx)
struct delta_ipc_header_msg {
u32 tag;
void *host_hdl;
u32 copro_hdl;
u32 command;
};
#define to_host_hdl(ctx) ((void *)ctx)
#define msg_to_ctx(msg) ((struct delta_ipc_ctx *)(msg)->header.host_hdl)
#define msg_to_copro_hdl(msg) ((msg)->header.copro_hdl)
static inline dma_addr_t to_paddr(struct delta_ipc_ctx *ctx, void *vaddr)
{
return (ctx->ipc_buf->paddr + (vaddr - ctx->ipc_buf->vaddr));
}
static inline bool is_valid_data(struct delta_ipc_ctx *ctx,
void *data, u32 size)
{
return ((data >= ctx->ipc_buf->vaddr) &&
((data + size) <= (ctx->ipc_buf->vaddr + ctx->ipc_buf->size)));
}
/*
* IPC shared memory (@ipc_buf_size, @ipc_buf_paddr) is sent to copro
* at each instance opening. This memory is allocated by IPC client
* and given through delta_ipc_open(). All messages parameters
* (open, set_stream, decode) will have their phy address within
* this IPC shared memory, avoiding de-facto recopies inside delta-ipc.
* All the below messages structures are used on both host and firmware
* side and are packed (use only of 32 bits size fields in messages
* structures to ensure packing):
* - struct delta_ipc_open_msg
* - struct delta_ipc_set_stream_msg
* - struct delta_ipc_decode_msg
* - struct delta_ipc_close_msg
* - struct delta_ipc_cb_msg
*/
struct delta_ipc_open_msg {
struct delta_ipc_header_msg header;
u32 ipc_buf_size;
dma_addr_t ipc_buf_paddr;
char name[32];
u32 param_size;
dma_addr_t param_paddr;
};
struct delta_ipc_set_stream_msg {
struct delta_ipc_header_msg header;
u32 param_size;
dma_addr_t param_paddr;
};
struct delta_ipc_decode_msg {
struct delta_ipc_header_msg header;
u32 param_size;
dma_addr_t param_paddr;
u32 status_size;
dma_addr_t status_paddr;
};
struct delta_ipc_close_msg {
struct delta_ipc_header_msg header;
};
struct delta_ipc_cb_msg {
struct delta_ipc_header_msg header;
int err;
};
static void build_msg_header(struct delta_ipc_ctx *ctx,
enum delta_ipc_fw_command command,
struct delta_ipc_header_msg *header)
{
header->tag = IPC_SANITY_TAG;
header->host_hdl = to_host_hdl(ctx);
header->copro_hdl = ctx->copro_hdl;
header->command = command;
}
int delta_ipc_open(struct delta_ctx *pctx, const char *name,
struct delta_ipc_param *param, u32 ipc_buf_size,
struct delta_buf **ipc_buf, void **hdl)
{
struct delta_dev *delta = pctx->dev;
struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
struct delta_ipc_ctx *ctx = &pctx->ipc_ctx;
struct delta_ipc_open_msg msg;
struct delta_buf *buf = &ctx->ipc_buf_struct;
int ret;
if (!rpmsg_device) {
dev_err(delta->dev,
"%s ipc: failed to open, rpmsg is not initialized\n",
pctx->name);
pctx->sys_errors++;
return -EINVAL;
}
if (!name) {
dev_err(delta->dev,
"%s ipc: failed to open, no name given\n",
pctx->name);
return -EINVAL;
}
if (!param || !param->data || !param->size) {
dev_err(delta->dev,
"%s ipc: failed to open, empty parameter\n",
pctx->name);
return -EINVAL;
}
if (!ipc_buf_size) {
dev_err(delta->dev,
"%s ipc: failed to open, no size given for ipc buffer\n",
pctx->name);
return -EINVAL;
}
if (param->size > ipc_buf_size) {
dev_err(delta->dev,
"%s ipc: failed to open, too large ipc parameter (%d bytes while max %d expected)\n",
pctx->name,
param->size, ctx->ipc_buf->size);
return -EINVAL;
}
/* init */
init_completion(&ctx->done);
/*
* allocation of contiguous buffer for
* data of commands exchanged between
* host and firmware coprocessor
*/
ret = hw_alloc(pctx, ipc_buf_size,
"ipc data buffer", buf);
if (ret)
return ret;
ctx->ipc_buf = buf;
/* build rpmsg message */
build_msg_header(ctx, DELTA_IPC_OPEN, &msg.header);
msg.ipc_buf_size = ipc_buf_size;
msg.ipc_buf_paddr = ctx->ipc_buf->paddr;
strscpy(msg.name, name, sizeof(msg.name));
msg.param_size = param->size;
memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size);
msg.param_paddr = ctx->ipc_buf->paddr;
/* send it */
ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
if (ret) {
dev_err(delta->dev,
"%s ipc: failed to open, rpmsg_send failed (%d) for DELTA_IPC_OPEN (name=%s, size=%d, data=%p)\n",
pctx->name,
ret, name, param->size, param->data);
goto err;
}
/* wait for acknowledge */
if (!wait_for_completion_timeout
(&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
dev_err(delta->dev,
"%s ipc: failed to open, timeout waiting for DELTA_IPC_OPEN callback (name=%s, size=%d, data=%p)\n",
pctx->name,
name, param->size, param->data);
ret = -ETIMEDOUT;
goto err;
}
/* command completed, check error */
if (ctx->cb_err) {
dev_err(delta->dev,
"%s ipc: failed to open, DELTA_IPC_OPEN completed but with error (%d) (name=%s, size=%d, data=%p)\n",
pctx->name,
ctx->cb_err, name, param->size, param->data);
ret = -EIO;
goto err;
}
*ipc_buf = ctx->ipc_buf;
*hdl = (void *)ctx;
return 0;
err:
pctx->sys_errors++;
hw_free(pctx, ctx->ipc_buf);
ctx->ipc_buf = NULL;
return ret;
};
int delta_ipc_set_stream(void *hdl, struct delta_ipc_param *param)
{
struct delta_ipc_ctx *ctx = to_ctx(hdl);
struct delta_ctx *pctx = to_pctx(ctx);
struct delta_dev *delta = pctx->dev;
struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
struct delta_ipc_set_stream_msg msg;
int ret;
if (!hdl) {
dev_err(delta->dev,
"%s ipc: failed to set stream, invalid ipc handle\n",
pctx->name);
return -EINVAL;
}
if (!rpmsg_device) {
dev_err(delta->dev,
"%s ipc: failed to set stream, rpmsg is not initialized\n",
pctx->name);
return -EINVAL;
}
if (!param || !param->data || !param->size) {
dev_err(delta->dev,
"%s ipc: failed to set stream, empty parameter\n",
pctx->name);
return -EINVAL;
}
if (param->size > ctx->ipc_buf->size) {
dev_err(delta->dev,
"%s ipc: failed to set stream, too large ipc parameter(%d bytes while max %d expected)\n",
pctx->name,
param->size, ctx->ipc_buf->size);
return -EINVAL;
}
if (!is_valid_data(ctx, param->data, param->size)) {
dev_err(delta->dev,
"%s ipc: failed to set stream, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
pctx->name,
param->size,
param->data,
ctx->ipc_buf->vaddr,
ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
return -EINVAL;
}
/* build rpmsg message */
build_msg_header(ctx, DELTA_IPC_SET_STREAM, &msg.header);
msg.param_size = param->size;
msg.param_paddr = to_paddr(ctx, param->data);
/* send it */
ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
if (ret) {
dev_err(delta->dev,
"%s ipc: failed to set stream, rpmsg_send failed (%d) for DELTA_IPC_SET_STREAM (size=%d, data=%p)\n",
pctx->name,
ret, param->size, param->data);
pctx->sys_errors++;
return ret;
}
/* wait for acknowledge */
if (!wait_for_completion_timeout
(&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
dev_err(delta->dev,
"%s ipc: failed to set stream, timeout waiting for DELTA_IPC_SET_STREAM callback (size=%d, data=%p)\n",
pctx->name,
param->size, param->data);
pctx->sys_errors++;
return -ETIMEDOUT;
}
/* command completed, check status */
if (ctx->cb_err) {
dev_err(delta->dev,
"%s ipc: failed to set stream, DELTA_IPC_SET_STREAM completed but with error (%d) (size=%d, data=%p)\n",
pctx->name,
ctx->cb_err, param->size, param->data);
pctx->sys_errors++;
return -EIO;
}
return 0;
}
int delta_ipc_decode(void *hdl, struct delta_ipc_param *param,
struct delta_ipc_param *status)
{
struct delta_ipc_ctx *ctx = to_ctx(hdl);
struct delta_ctx *pctx = to_pctx(ctx);
struct delta_dev *delta = pctx->dev;
struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
struct delta_ipc_decode_msg msg;
int ret;
if (!hdl) {
dev_err(delta->dev,
"%s ipc: failed to decode, invalid ipc handle\n",
pctx->name);
return -EINVAL;
}
if (!rpmsg_device) {
dev_err(delta->dev,
"%s ipc: failed to decode, rpmsg is not initialized\n",
pctx->name);
return -EINVAL;
}
if (!param || !param->data || !param->size) {
dev_err(delta->dev,
"%s ipc: failed to decode, empty parameter\n",
pctx->name);
return -EINVAL;
}
if (!status || !status->data || !status->size) {
dev_err(delta->dev,
"%s ipc: failed to decode, empty status\n",
pctx->name);
return -EINVAL;
}
if (param->size + status->size > ctx->ipc_buf->size) {
dev_err(delta->dev,
"%s ipc: failed to decode, too large ipc parameter (%d bytes (param) + %d bytes (status) while max %d expected)\n",
pctx->name,
param->size,
status->size,
ctx->ipc_buf->size);
return -EINVAL;
}
if (!is_valid_data(ctx, param->data, param->size)) {
dev_err(delta->dev,
"%s ipc: failed to decode, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
pctx->name,
param->size,
param->data,
ctx->ipc_buf->vaddr,
ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
return -EINVAL;
}
if (!is_valid_data(ctx, status->data, status->size)) {
dev_err(delta->dev,
"%s ipc: failed to decode, status is not in expected address range (size=%d, data=%p not in %p..%p)\n",
pctx->name,
status->size,
status->data,
ctx->ipc_buf->vaddr,
ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
return -EINVAL;
}
/* build rpmsg message */
build_msg_header(ctx, DELTA_IPC_DECODE, &msg.header);
msg.param_size = param->size;
msg.param_paddr = to_paddr(ctx, param->data);
msg.status_size = status->size;
msg.status_paddr = to_paddr(ctx, status->data);
/* send it */
ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
if (ret) {
dev_err(delta->dev,
"%s ipc: failed to decode, rpmsg_send failed (%d) for DELTA_IPC_DECODE (size=%d, data=%p)\n",
pctx->name,
ret, param->size, param->data);
pctx->sys_errors++;
return ret;
}
/* wait for acknowledge */
if (!wait_for_completion_timeout
(&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
dev_err(delta->dev,
"%s ipc: failed to decode, timeout waiting for DELTA_IPC_DECODE callback (size=%d, data=%p)\n",
pctx->name,
param->size, param->data);
pctx->sys_errors++;
return -ETIMEDOUT;
}
/* command completed, check status */
if (ctx->cb_err) {
dev_err(delta->dev,
"%s ipc: failed to decode, DELTA_IPC_DECODE completed but with error (%d) (size=%d, data=%p)\n",
pctx->name,
ctx->cb_err, param->size, param->data);
pctx->sys_errors++;
return -EIO;
}
return 0;
};
void delta_ipc_close(void *hdl)
{
struct delta_ipc_ctx *ctx = to_ctx(hdl);
struct delta_ctx *pctx = to_pctx(ctx);
struct delta_dev *delta = pctx->dev;
struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
struct delta_ipc_close_msg msg;
int ret;
if (!hdl) {
dev_err(delta->dev,
"%s ipc: failed to close, invalid ipc handle\n",
pctx->name);
return;
}
if (ctx->ipc_buf) {
hw_free(pctx, ctx->ipc_buf);
ctx->ipc_buf = NULL;
}
if (!rpmsg_device) {
dev_err(delta->dev,
"%s ipc: failed to close, rpmsg is not initialized\n",
pctx->name);
return;
}
/* build rpmsg message */
build_msg_header(ctx, DELTA_IPC_CLOSE, &msg.header);
/* send it */
ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
if (ret) {
dev_err(delta->dev,
"%s ipc: failed to close, rpmsg_send failed (%d) for DELTA_IPC_CLOSE\n",
pctx->name, ret);
pctx->sys_errors++;
return;
}
/* wait for acknowledge */
if (!wait_for_completion_timeout
(&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
dev_err(delta->dev,
"%s ipc: failed to close, timeout waiting for DELTA_IPC_CLOSE callback\n",
pctx->name);
pctx->sys_errors++;
return;
}
/* command completed, check status */
if (ctx->cb_err) {
dev_err(delta->dev,
"%s ipc: failed to close, DELTA_IPC_CLOSE completed but with error (%d)\n",
pctx->name, ctx->cb_err);
pctx->sys_errors++;
}
};
static int delta_ipc_cb(struct rpmsg_device *rpdev, void *data,
int len, void *priv, u32 src)
{
struct delta_ipc_ctx *ctx;
struct delta_ipc_cb_msg *msg;
/* sanity check */
if (!rpdev) {
dev_err(NULL, "rpdev is NULL\n");
return -EINVAL;
}
if (!data || !len) {
dev_err(&rpdev->dev,
"unexpected empty message received from src=%d\n", src);
return -EINVAL;
}
if (len != sizeof(*msg)) {
dev_err(&rpdev->dev,
"unexpected message length received from src=%d (received %d bytes while %zu bytes expected)\n",
len, src, sizeof(*msg));
return -EINVAL;
}
msg = (struct delta_ipc_cb_msg *)data;
if (msg->header.tag != IPC_SANITY_TAG) {
dev_err(&rpdev->dev,
"unexpected message tag received from src=%d (received %x tag while %x expected)\n",
src, msg->header.tag, IPC_SANITY_TAG);
return -EINVAL;
}
ctx = msg_to_ctx(msg);
if (!ctx) {
dev_err(&rpdev->dev,
"unexpected message with NULL host_hdl received from src=%d\n",
src);
return -EINVAL;
}
/*
* if not already known, save copro instance context
* to ensure re-entrance on copro side
*/
if (!ctx->copro_hdl)
ctx->copro_hdl = msg_to_copro_hdl(msg);
/*
* all is fine,
* update status & complete command
*/
ctx->cb_err = msg->err;
complete(&ctx->done);
return 0;
}
static int delta_ipc_probe(struct rpmsg_device *rpmsg_device)
{
struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);
struct delta_dev *delta = to_delta(rpdrv);
delta->rpmsg_device = rpmsg_device;
return 0;
}
static void delta_ipc_remove(struct rpmsg_device *rpmsg_device)
{
struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);
struct delta_dev *delta = to_delta(rpdrv);
delta->rpmsg_device = NULL;
}
static struct rpmsg_device_id delta_ipc_device_id_table[] = {
{.name = "rpmsg-delta"},
{},
};
static struct rpmsg_driver delta_rpmsg_driver = {
.drv = {.name = KBUILD_MODNAME},
.id_table = delta_ipc_device_id_table,
.probe = delta_ipc_probe,
.callback = delta_ipc_cb,
.remove = delta_ipc_remove,
};
int delta_ipc_init(struct delta_dev *delta)
{
delta->rpmsg_driver = delta_rpmsg_driver;
return register_rpmsg_driver(&delta->rpmsg_driver);
}
void delta_ipc_exit(struct delta_dev *delta)
{
unregister_rpmsg_driver(&delta->rpmsg_driver);
}
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#ifndef DELTA_IPC_H
#define DELTA_IPC_H
int delta_ipc_init(struct delta_dev *delta);
void delta_ipc_exit(struct delta_dev *delta);
/*
* delta_ipc_open - open a decoding instance on firmware side
* @ctx: (in) delta context
* @name: (in) name of decoder to be used
* @param: (in) open command parameters specific to decoder
* @param.size: (in) size of parameter
* @param.data: (in) virtual address of parameter
* @ipc_buf_size: (in) size of IPC shared buffer between host
* and copro used to share command data.
* Client have to set here the size of the biggest
* command parameters (+ status if any).
* Allocation will be done in this function which
* will give back to client in @ipc_buf the virtual
* & physical addresses & size of shared IPC buffer.
* All the further command data (parameters + status)
* have to be written in this shared IPC buffer
* virtual memory. This is done to avoid
* unnecessary copies of command data.
* @ipc_buf: (out) allocated IPC shared buffer
* @ipc_buf.size: (out) allocated size
* @ipc_buf.vaddr: (out) virtual address where to copy
* further command data
* @hdl: (out) handle of decoding instance.
*/
int delta_ipc_open(struct delta_ctx *ctx, const char *name,
struct delta_ipc_param *param, u32 ipc_buf_size,
struct delta_buf **ipc_buf, void **hdl);
/*
* delta_ipc_set_stream - set information about stream to decoder
* @hdl: (in) handle of decoding instance.
* @param: (in) set stream command parameters specific to decoder
* @param.size: (in) size of parameter
* @param.data: (in) virtual address of parameter. Must be
* within IPC shared buffer range
*/
int delta_ipc_set_stream(void *hdl, struct delta_ipc_param *param);
/*
* delta_ipc_decode - frame decoding synchronous request, returns only
* after decoding completion on firmware side.
* @hdl: (in) handle of decoding instance.
* @param: (in) decode command parameters specific to decoder
* @param.size: (in) size of parameter
* @param.data: (in) virtual address of parameter. Must be
* within IPC shared buffer range
* @status: (in/out) decode command status specific to decoder
* @status.size: (in) size of status
* @status.data: (in/out) virtual address of status. Must be
* within IPC shared buffer range.
* Status is filled by decoding instance
* after decoding completion.
*/
int delta_ipc_decode(void *hdl, struct delta_ipc_param *param,
struct delta_ipc_param *status);
/*
* delta_ipc_close - close decoding instance
* @hdl: (in) handle of decoding instance to close.
*/
void delta_ipc_close(void *hdl);
#endif /* DELTA_IPC_H */
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#include "delta.h"
#include "delta-mem.h"
int hw_alloc(struct delta_ctx *ctx, u32 size, const char *name,
struct delta_buf *buf)
{
struct delta_dev *delta = ctx->dev;
dma_addr_t dma_addr;
void *addr;
unsigned long attrs = DMA_ATTR_WRITE_COMBINE;
addr = dma_alloc_attrs(delta->dev, size, &dma_addr,
GFP_KERNEL | __GFP_NOWARN, attrs);
if (!addr) {
dev_err(delta->dev,
"%s hw_alloc:dma_alloc_coherent failed for %s (size=%d)\n",
ctx->name, name, size);
ctx->sys_errors++;
return -ENOMEM;
}
buf->size = size;
buf->paddr = dma_addr;
buf->vaddr = addr;
buf->name = name;
buf->attrs = attrs;
dev_dbg(delta->dev,
"%s allocate %d bytes of HW memory @(virt=0x%p, phy=0x%pad): %s\n",
ctx->name, size, buf->vaddr, &buf->paddr, buf->name);
return 0;
}
void hw_free(struct delta_ctx *ctx, struct delta_buf *buf)
{
struct delta_dev *delta = ctx->dev;
dev_dbg(delta->dev,
"%s free %d bytes of HW memory @(virt=0x%p, phy=0x%pad): %s\n",
ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name);
dma_free_attrs(delta->dev, buf->size,
buf->vaddr, buf->paddr, buf->attrs);
}
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#ifndef DELTA_MEM_H
#define DELTA_MEM_H
int hw_alloc(struct delta_ctx *ctx, u32 size, const char *name,
struct delta_buf *buf);
void hw_free(struct delta_ctx *ctx, struct delta_buf *buf);
#endif /* DELTA_MEM_H */
@@ -0,0 +1,457 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2013
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#include <linux/slab.h>
#include "delta.h"
#include "delta-ipc.h"
#include "delta-mjpeg.h"
#include "delta-mjpeg-fw.h"
#define DELTA_MJPEG_MAX_RESO DELTA_MAX_RESO
struct delta_mjpeg_ctx {
/* jpeg header */
struct mjpeg_header header_struct;
struct mjpeg_header *header;
/* ipc */
void *ipc_hdl;
struct delta_buf *ipc_buf;
/* decoded output frame */
struct delta_frame *out_frame;
unsigned char str[3000];
};
#define to_ctx(ctx) ((struct delta_mjpeg_ctx *)(ctx)->priv)
static char *ipc_open_param_str(struct jpeg_video_decode_init_params_t *p,
char *str, unsigned int len)
{
char *b = str;
if (!p)
return "";
b += snprintf(b, len,
"jpeg_video_decode_init_params_t\n"
"circular_buffer_begin_addr_p 0x%x\n"
"circular_buffer_end_addr_p 0x%x\n",
p->circular_buffer_begin_addr_p,
p->circular_buffer_end_addr_p);
return str;
}
static char *ipc_decode_param_str(struct jpeg_decode_params_t *p,
char *str, unsigned int len)
{
char *b = str;
if (!p)
return "";
b += snprintf(b, len,
"jpeg_decode_params_t\n"
"picture_start_addr_p 0x%x\n"
"picture_end_addr_p 0x%x\n"
"decoding_mode %d\n"
"display_buffer_addr.display_decimated_luma_p 0x%x\n"
"display_buffer_addr.display_decimated_chroma_p 0x%x\n"
"main_aux_enable %d\n"
"additional_flags 0x%x\n"
"field_flag %x\n"
"is_jpeg_image %x\n",
p->picture_start_addr_p,
p->picture_end_addr_p,
p->decoding_mode,
p->display_buffer_addr.display_decimated_luma_p,
p->display_buffer_addr.display_decimated_chroma_p,
p->main_aux_enable, p->additional_flags,
p->field_flag,
p->is_jpeg_image);
return str;
}
static inline bool is_stream_error(enum jpeg_decoding_error_t err)
{
switch (err) {
case JPEG_DECODER_UNDEFINED_HUFF_TABLE:
case JPEG_DECODER_BAD_RESTART_MARKER:
case JPEG_DECODER_BAD_SOS_SPECTRAL:
case JPEG_DECODER_BAD_SOS_SUCCESSIVE:
case JPEG_DECODER_BAD_HEADER_LENGTH:
case JPEG_DECODER_BAD_COUNT_VALUE:
case JPEG_DECODER_BAD_DHT_MARKER:
case JPEG_DECODER_BAD_INDEX_VALUE:
case JPEG_DECODER_BAD_NUMBER_HUFFMAN_TABLES:
case JPEG_DECODER_BAD_QUANT_TABLE_LENGTH:
case JPEG_DECODER_BAD_NUMBER_QUANT_TABLES:
case JPEG_DECODER_BAD_COMPONENT_COUNT:
return true;
default:
return false;
}
}
static inline const char *err_str(enum jpeg_decoding_error_t err)
{
switch (err) {
case JPEG_DECODER_NO_ERROR:
return "JPEG_DECODER_NO_ERROR";
case JPEG_DECODER_UNDEFINED_HUFF_TABLE:
return "JPEG_DECODER_UNDEFINED_HUFF_TABLE";
case JPEG_DECODER_UNSUPPORTED_MARKER:
return "JPEG_DECODER_UNSUPPORTED_MARKER";
case JPEG_DECODER_UNABLE_ALLOCATE_MEMORY:
return "JPEG_DECODER_UNABLE_ALLOCATE_MEMORY";
case JPEG_DECODER_NON_SUPPORTED_SAMP_FACTORS:
return "JPEG_DECODER_NON_SUPPORTED_SAMP_FACTORS";
case JPEG_DECODER_BAD_PARAMETER:
return "JPEG_DECODER_BAD_PARAMETER";
case JPEG_DECODER_DECODE_ERROR:
return "JPEG_DECODER_DECODE_ERROR";
case JPEG_DECODER_BAD_RESTART_MARKER:
return "JPEG_DECODER_BAD_RESTART_MARKER";
case JPEG_DECODER_UNSUPPORTED_COLORSPACE:
return "JPEG_DECODER_UNSUPPORTED_COLORSPACE";
case JPEG_DECODER_BAD_SOS_SPECTRAL:
return "JPEG_DECODER_BAD_SOS_SPECTRAL";
case JPEG_DECODER_BAD_SOS_SUCCESSIVE:
return "JPEG_DECODER_BAD_SOS_SUCCESSIVE";
case JPEG_DECODER_BAD_HEADER_LENGTH:
return "JPEG_DECODER_BAD_HEADER_LENGTH";
case JPEG_DECODER_BAD_COUNT_VALUE:
return "JPEG_DECODER_BAD_COUNT_VALUE";
case JPEG_DECODER_BAD_DHT_MARKER:
return "JPEG_DECODER_BAD_DHT_MARKER";
case JPEG_DECODER_BAD_INDEX_VALUE:
return "JPEG_DECODER_BAD_INDEX_VALUE";
case JPEG_DECODER_BAD_NUMBER_HUFFMAN_TABLES:
return "JPEG_DECODER_BAD_NUMBER_HUFFMAN_TABLES";
case JPEG_DECODER_BAD_QUANT_TABLE_LENGTH:
return "JPEG_DECODER_BAD_QUANT_TABLE_LENGTH";
case JPEG_DECODER_BAD_NUMBER_QUANT_TABLES:
return "JPEG_DECODER_BAD_NUMBER_QUANT_TABLES";
case JPEG_DECODER_BAD_COMPONENT_COUNT:
return "JPEG_DECODER_BAD_COMPONENT_COUNT";
case JPEG_DECODER_DIVIDE_BY_ZERO_ERROR:
return "JPEG_DECODER_DIVIDE_BY_ZERO_ERROR";
case JPEG_DECODER_NOT_JPG_IMAGE:
return "JPEG_DECODER_NOT_JPG_IMAGE";
case JPEG_DECODER_UNSUPPORTED_ROTATION_ANGLE:
return "JPEG_DECODER_UNSUPPORTED_ROTATION_ANGLE";
case JPEG_DECODER_UNSUPPORTED_SCALING:
return "JPEG_DECODER_UNSUPPORTED_SCALING";
case JPEG_DECODER_INSUFFICIENT_OUTPUTBUFFER_SIZE:
return "JPEG_DECODER_INSUFFICIENT_OUTPUTBUFFER_SIZE";
case JPEG_DECODER_BAD_HWCFG_GP_VERSION_VALUE:
return "JPEG_DECODER_BAD_HWCFG_GP_VERSION_VALUE";
case JPEG_DECODER_BAD_VALUE_FROM_RED:
return "JPEG_DECODER_BAD_VALUE_FROM_RED";
case JPEG_DECODER_BAD_SUBREGION_PARAMETERS:
return "JPEG_DECODER_BAD_SUBREGION_PARAMETERS";
case JPEG_DECODER_PROGRESSIVE_DECODE_NOT_SUPPORTED:
return "JPEG_DECODER_PROGRESSIVE_DECODE_NOT_SUPPORTED";
case JPEG_DECODER_ERROR_TASK_TIMEOUT:
return "JPEG_DECODER_ERROR_TASK_TIMEOUT";
case JPEG_DECODER_ERROR_FEATURE_NOT_SUPPORTED:
return "JPEG_DECODER_ERROR_FEATURE_NOT_SUPPORTED";
default:
return "!unknown MJPEG error!";
}
}
static bool delta_mjpeg_check_status(struct delta_ctx *pctx,
struct jpeg_decode_return_params_t *status)
{
struct delta_dev *delta = pctx->dev;
bool dump = false;
if (status->error_code == JPEG_DECODER_NO_ERROR)
goto out;
if (is_stream_error(status->error_code)) {
dev_warn_ratelimited(delta->dev,
"%s firmware: stream error @ frame %d (%s)\n",
pctx->name, pctx->decoded_frames,
err_str(status->error_code));
pctx->stream_errors++;
} else {
dev_warn_ratelimited(delta->dev,
"%s firmware: decode error @ frame %d (%s)\n",
pctx->name, pctx->decoded_frames,
err_str(status->error_code));
pctx->decode_errors++;
dump = true;
}
out:
dev_dbg(delta->dev,
"%s firmware: decoding time(us)=%d\n", pctx->name,
status->decode_time_in_us);
return dump;
}
static int delta_mjpeg_ipc_open(struct delta_ctx *pctx)
{
struct delta_dev *delta = pctx->dev;
struct delta_mjpeg_ctx *ctx = to_ctx(pctx);
int ret = 0;
struct jpeg_video_decode_init_params_t params_struct;
struct jpeg_video_decode_init_params_t *params = &params_struct;
struct delta_buf *ipc_buf;
u32 ipc_buf_size;
struct delta_ipc_param ipc_param;
void *hdl;
memset(params, 0, sizeof(*params));
params->circular_buffer_begin_addr_p = 0x00000000;
params->circular_buffer_end_addr_p = 0xffffffff;
dev_vdbg(delta->dev,
"%s %s\n", pctx->name,
ipc_open_param_str(params, ctx->str, sizeof(ctx->str)));
ipc_param.size = sizeof(*params);
ipc_param.data = params;
ipc_buf_size = sizeof(struct jpeg_decode_params_t) +
sizeof(struct jpeg_decode_return_params_t);
ret = delta_ipc_open(pctx, "JPEG_DECODER_HW0", &ipc_param,
ipc_buf_size, &ipc_buf, &hdl);
if (ret) {
dev_err(delta->dev,
"%s dumping command %s\n", pctx->name,
ipc_open_param_str(params, ctx->str, sizeof(ctx->str)));
return ret;
}
ctx->ipc_buf = ipc_buf;
ctx->ipc_hdl = hdl;
return 0;
}
static int delta_mjpeg_ipc_decode(struct delta_ctx *pctx, dma_addr_t pstart, dma_addr_t pend)
{
struct delta_dev *delta = pctx->dev;
struct delta_mjpeg_ctx *ctx = to_ctx(pctx);
int ret = 0;
struct jpeg_decode_params_t *params = ctx->ipc_buf->vaddr;
struct jpeg_decode_return_params_t *status =
ctx->ipc_buf->vaddr + sizeof(*params);
struct delta_frame *frame;
struct delta_ipc_param ipc_param, ipc_status;
ret = delta_get_free_frame(pctx, &frame);
if (ret)
return ret;
memset(params, 0, sizeof(*params));
params->picture_start_addr_p = pstart;
params->picture_end_addr_p = pend;
/*
* !WARNING!
* the NV12 decoded frame is only available
* on decimated output when enabling flag
* "JPEG_ADDITIONAL_FLAG_420MB"...
* the non decimated output gives YUV422SP
*/
params->main_aux_enable = JPEG_DISP_AUX_EN;
params->additional_flags = JPEG_ADDITIONAL_FLAG_420MB;
params->horizontal_decimation_factor = JPEG_HDEC_1;
params->vertical_decimation_factor = JPEG_VDEC_1;
params->decoding_mode = JPEG_NORMAL_DECODE;
params->display_buffer_addr.struct_size =
sizeof(struct jpeg_display_buffer_address_t);
params->display_buffer_addr.display_decimated_luma_p =
(u32)frame->paddr;
params->display_buffer_addr.display_decimated_chroma_p =
(u32)(frame->paddr
+ frame->info.aligned_width * frame->info.aligned_height);
dev_vdbg(delta->dev,
"%s %s\n", pctx->name,
ipc_decode_param_str(params, ctx->str, sizeof(ctx->str)));
/* status */
memset(status, 0, sizeof(*status));
status->error_code = JPEG_DECODER_NO_ERROR;
ipc_param.size = sizeof(*params);
ipc_param.data = params;
ipc_status.size = sizeof(*status);
ipc_status.data = status;
ret = delta_ipc_decode(ctx->ipc_hdl, &ipc_param, &ipc_status);
if (ret) {
dev_err(delta->dev,
"%s dumping command %s\n", pctx->name,
ipc_decode_param_str(params, ctx->str,
sizeof(ctx->str)));
return ret;
}
pctx->decoded_frames++;
/* check firmware decoding status */
if (delta_mjpeg_check_status(pctx, status)) {
dev_err(delta->dev,
"%s dumping command %s\n", pctx->name,
ipc_decode_param_str(params, ctx->str,
sizeof(ctx->str)));
}
frame->field = V4L2_FIELD_NONE;
frame->flags = V4L2_BUF_FLAG_KEYFRAME;
frame->state |= DELTA_FRAME_DEC;
ctx->out_frame = frame;
return 0;
}
static int delta_mjpeg_open(struct delta_ctx *pctx)
{
struct delta_mjpeg_ctx *ctx;
ctx = kzalloc_obj(*ctx);
if (!ctx)
return -ENOMEM;
pctx->priv = ctx;
return 0;
}
static int delta_mjpeg_close(struct delta_ctx *pctx)
{
struct delta_mjpeg_ctx *ctx = to_ctx(pctx);
if (ctx->ipc_hdl) {
delta_ipc_close(ctx->ipc_hdl);
ctx->ipc_hdl = NULL;
}
kfree(ctx);
return 0;
}
static int delta_mjpeg_get_streaminfo(struct delta_ctx *pctx,
struct delta_streaminfo *streaminfo)
{
struct delta_mjpeg_ctx *ctx = to_ctx(pctx);
if (!ctx->header)
goto nodata;
streaminfo->streamformat = V4L2_PIX_FMT_MJPEG;
streaminfo->width = ctx->header->frame_width;
streaminfo->height = ctx->header->frame_height;
/* progressive stream */
streaminfo->field = V4L2_FIELD_NONE;
streaminfo->dpb = 1;
return 0;
nodata:
return -ENODATA;
}
static int delta_mjpeg_decode(struct delta_ctx *pctx, struct delta_au *pau)
{
struct delta_dev *delta = pctx->dev;
struct delta_mjpeg_ctx *ctx = to_ctx(pctx);
int ret;
void *au_vaddr = pau->vaddr;
dma_addr_t au_dma = pau->paddr;
size_t au_size = pau->size;
unsigned int data_offset = 0;
struct mjpeg_header *header = &ctx->header_struct;
if (!ctx->header) {
ret = delta_mjpeg_read_header(pctx, au_vaddr, au_size,
header, &data_offset);
if (ret) {
pctx->stream_errors++;
goto err;
}
if (header->frame_width * header->frame_height >
DELTA_MJPEG_MAX_RESO) {
dev_err(delta->dev,
"%s stream resolution too large: %dx%d > %d pixels budget\n",
pctx->name,
header->frame_width,
header->frame_height, DELTA_MJPEG_MAX_RESO);
ret = -EINVAL;
goto err;
}
ctx->header = header;
goto out;
}
if (!ctx->ipc_hdl) {
ret = delta_mjpeg_ipc_open(pctx);
if (ret)
goto err;
}
ret = delta_mjpeg_read_header(pctx, au_vaddr, au_size,
ctx->header, &data_offset);
if (ret) {
pctx->stream_errors++;
goto err;
}
au_dma += data_offset;
au_vaddr += data_offset;
ret = delta_mjpeg_ipc_decode(pctx, au_dma, au_dma + au_size - 1);
if (ret)
goto err;
out:
return 0;
err:
return ret;
}
static int delta_mjpeg_get_frame(struct delta_ctx *pctx,
struct delta_frame **frame)
{
struct delta_mjpeg_ctx *ctx = to_ctx(pctx);
if (!ctx->out_frame)
return -ENODATA;
*frame = ctx->out_frame;
ctx->out_frame = NULL;
return 0;
}
const struct delta_dec mjpegdec = {
.name = "MJPEG",
.streamformat = V4L2_PIX_FMT_MJPEG,
.pixelformat = V4L2_PIX_FMT_NV12,
.open = delta_mjpeg_open,
.close = delta_mjpeg_close,
.get_streaminfo = delta_mjpeg_get_streaminfo,
.get_frameinfo = delta_get_frameinfo_default,
.decode = delta_mjpeg_decode,
.get_frame = delta_mjpeg_get_frame,
.recycle = delta_recycle_default,
};
@@ -0,0 +1,225 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#ifndef DELTA_MJPEG_FW_H
#define DELTA_MJPEG_FW_H
/*
* struct jpeg_decoded_buffer_address_t
*
* defines the addresses where the decoded picture/additional
* info related to the block structures will be stored
*
* @display_luma_p: address of the luma buffer
* @display_chroma_p: address of the chroma buffer
*/
struct jpeg_decoded_buffer_address_t {
u32 luma_p;
u32 chroma_p;
};
/*
* struct jpeg_display_buffer_address_t
*
* defines the addresses (used by the Display Reconstruction block)
* where the pictures to be displayed will be stored
*
* @struct_size: size of the structure in bytes
* @display_luma_p: address of the luma buffer
* @display_chroma_p: address of the chroma buffer
* @display_decimated_luma_p: address of the decimated luma buffer
* @display_decimated_chroma_p: address of the decimated chroma buffer
*/
struct jpeg_display_buffer_address_t {
u32 struct_size;
u32 display_luma_p;
u32 display_chroma_p;
u32 display_decimated_luma_p;
u32 display_decimated_chroma_p;
};
/*
* used for enabling main/aux outputs for both display &
* reference reconstruction blocks
*/
enum jpeg_rcn_ref_disp_enable_t {
/* enable decimated (for display) reconstruction */
JPEG_DISP_AUX_EN = 0x00000010,
/* enable main (for display) reconstruction */
JPEG_DISP_MAIN_EN = 0x00000020,
/* enable both main & decimated (for display) reconstruction */
JPEG_DISP_AUX_MAIN_EN = 0x00000030,
/* enable only reference output(ex. for trick modes) */
JPEG_REF_MAIN_EN = 0x00000100,
/*
* enable reference output with decimated
* (for display) reconstruction
*/
JPEG_REF_MAIN_DISP_AUX_EN = 0x00000110,
/*
* enable reference output with main
* (for display) reconstruction
*/
JPEG_REF_MAIN_DISP_MAIN_EN = 0x00000120,
/*
* enable reference output with main & decimated
* (for display) reconstruction
*/
JPEG_REF_MAIN_DISP_MAIN_AUX_EN = 0x00000130
};
/* identifies the horizontal decimation factor */
enum jpeg_horizontal_deci_factor_t {
/* no resize */
JPEG_HDEC_1 = 0x00000000,
/* Advanced H/2 resize using improved 8-tap filters */
JPEG_HDEC_ADVANCED_2 = 0x00000101,
/* Advanced H/4 resize using improved 8-tap filters */
JPEG_HDEC_ADVANCED_4 = 0x00000102
};
/* identifies the vertical decimation factor */
enum jpeg_vertical_deci_factor_t {
/* no resize */
JPEG_VDEC_1 = 0x00000000,
/* V/2 , progressive resize */
JPEG_VDEC_ADVANCED_2_PROG = 0x00000204,
/* V/2 , interlaced resize */
JPEG_VDEC_ADVANCED_2_INT = 0x000000208
};
/* status of the decoding process */
enum jpeg_decoding_error_t {
JPEG_DECODER_NO_ERROR = 0,
JPEG_DECODER_UNDEFINED_HUFF_TABLE = 1,
JPEG_DECODER_UNSUPPORTED_MARKER = 2,
JPEG_DECODER_UNABLE_ALLOCATE_MEMORY = 3,
JPEG_DECODER_NON_SUPPORTED_SAMP_FACTORS = 4,
JPEG_DECODER_BAD_PARAMETER = 5,
JPEG_DECODER_DECODE_ERROR = 6,
JPEG_DECODER_BAD_RESTART_MARKER = 7,
JPEG_DECODER_UNSUPPORTED_COLORSPACE = 8,
JPEG_DECODER_BAD_SOS_SPECTRAL = 9,
JPEG_DECODER_BAD_SOS_SUCCESSIVE = 10,
JPEG_DECODER_BAD_HEADER_LENGTH = 11,
JPEG_DECODER_BAD_COUNT_VALUE = 12,
JPEG_DECODER_BAD_DHT_MARKER = 13,
JPEG_DECODER_BAD_INDEX_VALUE = 14,
JPEG_DECODER_BAD_NUMBER_HUFFMAN_TABLES = 15,
JPEG_DECODER_BAD_QUANT_TABLE_LENGTH = 16,
JPEG_DECODER_BAD_NUMBER_QUANT_TABLES = 17,
JPEG_DECODER_BAD_COMPONENT_COUNT = 18,
JPEG_DECODER_DIVIDE_BY_ZERO_ERROR = 19,
JPEG_DECODER_NOT_JPG_IMAGE = 20,
JPEG_DECODER_UNSUPPORTED_ROTATION_ANGLE = 21,
JPEG_DECODER_UNSUPPORTED_SCALING = 22,
JPEG_DECODER_INSUFFICIENT_OUTPUTBUFFER_SIZE = 23,
JPEG_DECODER_BAD_HWCFG_GP_VERSION_VALUE = 24,
JPEG_DECODER_BAD_VALUE_FROM_RED = 25,
JPEG_DECODER_BAD_SUBREGION_PARAMETERS = 26,
JPEG_DECODER_PROGRESSIVE_DECODE_NOT_SUPPORTED = 27,
JPEG_DECODER_ERROR_TASK_TIMEOUT = 28,
JPEG_DECODER_ERROR_FEATURE_NOT_SUPPORTED = 29
};
/* identifies the decoding mode */
enum jpeg_decoding_mode_t {
JPEG_NORMAL_DECODE = 0,
};
enum jpeg_additional_flags_t {
JPEG_ADDITIONAL_FLAG_NONE = 0,
/* request firmware to return values of the CEH registers */
JPEG_ADDITIONAL_FLAG_CEH = 1,
/* output storage of auxiliary reconstruction in Raster format. */
JPEG_ADDITIONAL_FLAG_RASTER = 64,
/* output storage of auxiliary reconstruction in 420MB format. */
JPEG_ADDITIONAL_FLAG_420MB = 128
};
/*
* struct jpeg_video_decode_init_params_t - initialization command parameters
*
* @circular_buffer_begin_addr_p: start address of fw circular buffer
* @circular_buffer_end_addr_p: end address of fw circular buffer
*/
struct jpeg_video_decode_init_params_t {
u32 circular_buffer_begin_addr_p;
u32 circular_buffer_end_addr_p;
u32 reserved;
};
/*
* struct jpeg_decode_params_t - decode command parameters
*
* @picture_start_addr_p: start address of jpeg picture
* @picture_end_addr_p: end address of jpeg picture
* @decoded_buffer_addr: decoded picture buffer
* @display_buffer_addr: display picture buffer
* @main_aux_enable: enable main and/or aux outputs
* @horizontal_decimation_factor:horizontal decimation factor
* @vertical_decimation_factor: vertical decimation factor
* @xvalue0: the x(0) coordinate for subregion decoding
* @xvalue1: the x(1) coordinate for subregion decoding
* @yvalue0: the y(0) coordinate for subregion decoding
* @yvalue1: the y(1) coordinate for subregion decoding
* @decoding_mode: decoding mode
* @additional_flags: additional flags
* @field_flag: determines frame/field scan
* @is_jpeg_image: 1 = still jpeg, 0 = motion jpeg
*/
struct jpeg_decode_params_t {
u32 picture_start_addr_p;
u32 picture_end_addr_p;
struct jpeg_decoded_buffer_address_t decoded_buffer_addr;
struct jpeg_display_buffer_address_t display_buffer_addr;
enum jpeg_rcn_ref_disp_enable_t main_aux_enable;
enum jpeg_horizontal_deci_factor_t horizontal_decimation_factor;
enum jpeg_vertical_deci_factor_t vertical_decimation_factor;
u32 xvalue0;
u32 xvalue1;
u32 yvalue0;
u32 yvalue1;
enum jpeg_decoding_mode_t decoding_mode;
u32 additional_flags;
u32 field_flag;
u32 reserved;
u32 is_jpeg_image;
};
/*
* struct jpeg_decode_return_params_t
*
* status returned by firmware after decoding
*
* @decode_time_in_us: decoding time in microseconds
* @pm_cycles: profiling information
* @pm_dmiss: profiling information
* @pm_imiss: profiling information
* @pm_bundles: profiling information
* @pm_pft: profiling information
* @error_code: status of the decoding process
* @ceh_registers: array where values of the Contrast Enhancement
* Histogram (CEH) registers will be stored.
* ceh_registers[0] correspond to register MBE_CEH_0_7,
* ceh_registers[1] correspond to register MBE_CEH_8_15
* ceh_registers[2] correspond to register MBE_CEH_16_23
* Note that elements of this array will be updated only
* if additional_flags has JPEG_ADDITIONAL_FLAG_CEH set.
*/
struct jpeg_decode_return_params_t {
/* profiling info */
u32 decode_time_in_us;
u32 pm_cycles;
u32 pm_dmiss;
u32 pm_imiss;
u32 pm_bundles;
u32 pm_pft;
enum jpeg_decoding_error_t error_code;
u32 ceh_registers[32];
};
#endif /* DELTA_MJPEG_FW_H */
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2013
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#include "delta.h"
#include "delta-mjpeg.h"
#define MJPEG_SOF_0 0xc0
#define MJPEG_SOF_1 0xc1
#define MJPEG_SOI 0xd8
#define MJPEG_MARKER 0xff
static char *header_str(struct mjpeg_header *header,
char *str,
unsigned int len)
{
char *cur = str;
unsigned int left = len;
if (!header)
return "";
snprintf(cur, left, "[MJPEG header]\n"
"|- length = %d\n"
"|- precision = %d\n"
"|- width = %d\n"
"|- height = %d\n"
"|- components = %d\n",
header->length,
header->sample_precision,
header->frame_width,
header->frame_height,
header->nb_of_components);
return str;
}
static int delta_mjpeg_read_sof(struct delta_ctx *pctx,
unsigned char *data, unsigned int size,
struct mjpeg_header *header)
{
struct delta_dev *delta = pctx->dev;
unsigned int offset = 0;
if (size < 64)
goto err_no_more;
memset(header, 0, sizeof(*header));
header->length = be16_to_cpu(*(__be16 *)(data + offset));
offset += sizeof(u16);
header->sample_precision = *(u8 *)(data + offset);
offset += sizeof(u8);
header->frame_height = be16_to_cpu(*(__be16 *)(data + offset));
offset += sizeof(u16);
header->frame_width = be16_to_cpu(*(__be16 *)(data + offset));
offset += sizeof(u16);
header->nb_of_components = *(u8 *)(data + offset);
offset += sizeof(u8);
if (header->nb_of_components >= MJPEG_MAX_COMPONENTS) {
dev_err(delta->dev,
"%s unsupported number of components (%d > %d)\n",
pctx->name, header->nb_of_components,
MJPEG_MAX_COMPONENTS);
return -EINVAL;
}
if ((offset + header->nb_of_components *
sizeof(header->components[0])) > size)
goto err_no_more;
return 0;
err_no_more:
dev_err(delta->dev,
"%s sof: reached end of %d size input stream\n",
pctx->name, size);
return -ENODATA;
}
int delta_mjpeg_read_header(struct delta_ctx *pctx,
unsigned char *data, unsigned int size,
struct mjpeg_header *header,
unsigned int *data_offset)
{
struct delta_dev *delta = pctx->dev;
unsigned char str[200];
unsigned int ret = 0;
unsigned int offset = 0;
unsigned int soi = 0;
if (size < 2)
goto err_no_more;
offset = 0;
while (1) {
if (data[offset] == MJPEG_MARKER)
switch (data[offset + 1]) {
case MJPEG_SOI:
soi = 1;
*data_offset = offset;
break;
case MJPEG_SOF_0:
case MJPEG_SOF_1:
if (!soi) {
dev_err(delta->dev,
"%s wrong sequence, got SOF while SOI not seen\n",
pctx->name);
return -EINVAL;
}
ret = delta_mjpeg_read_sof(pctx,
&data[offset + 2],
size - (offset + 2),
header);
if (ret)
goto err;
goto done;
default:
break;
}
offset++;
if ((offset + 2) >= size)
goto err_no_more;
}
done:
dev_dbg(delta->dev,
"%s found header @ offset %d:\n%s", pctx->name,
*data_offset,
header_str(header, str, sizeof(str)));
return 0;
err_no_more:
dev_err(delta->dev,
"%s no header found within %d bytes input stream\n",
pctx->name, size);
return -ENODATA;
err:
return ret;
}
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2013
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#ifndef DELTA_MJPEG_H
#define DELTA_MJPEG_H
#include "delta.h"
struct mjpeg_component {
unsigned int id;/* 1=Y, 2=Cb, 3=Cr, 4=L, 5=Q */
unsigned int h_sampling_factor;
unsigned int v_sampling_factor;
unsigned int quant_table_index;
};
#define MJPEG_MAX_COMPONENTS 5
struct mjpeg_header {
unsigned int length;
unsigned int sample_precision;
unsigned int frame_width;
unsigned int frame_height;
unsigned int nb_of_components;
struct mjpeg_component components[MJPEG_MAX_COMPONENTS];
};
int delta_mjpeg_read_header(struct delta_ctx *pctx,
unsigned char *data, unsigned int size,
struct mjpeg_header *header,
unsigned int *data_offset);
#endif /* DELTA_MJPEG_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,566 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
#ifndef DELTA_H
#define DELTA_H
#include <linux/rpmsg.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include "delta-cfg.h"
/*
* enum delta_state - state of decoding instance
*
*@DELTA_STATE_WF_FORMAT:
* Wait for compressed format to be set by V4L2 client in order
* to know what is the relevant decoder to open.
*
*@DELTA_STATE_WF_STREAMINFO:
* Wait for stream information to be available (bitstream
* header parsing is done).
*
*@DELTA_STATE_READY:
* Decoding instance is ready to decode compressed access unit.
*
*@DELTA_STATE_WF_EOS:
* Decoding instance is waiting for EOS (End Of Stream) completion.
*
*@DELTA_STATE_EOS:
* EOS (End Of Stream) is completed (signaled to user). Decoding instance
* should then be closed.
*/
enum delta_state {
DELTA_STATE_WF_FORMAT,
DELTA_STATE_WF_STREAMINFO,
DELTA_STATE_READY,
DELTA_STATE_WF_EOS,
DELTA_STATE_EOS
};
/*
* struct delta_streaminfo - information about stream to decode
*
* @flags: validity of fields (crop, pixelaspect, other)
* @width: width of video stream
* @height: height ""
* @streamformat: fourcc compressed format of video (MJPEG, MPEG2, ...)
* @dpb: number of frames needed to decode a single frame
* (h264 dpb, up to 16)
* @crop: cropping window inside decoded frame (1920x1080@0,0
* inside 1920x1088 frame for ex.)
* @pixelaspect: pixel aspect ratio of video (4/3, 5/4)
* @field: interlaced or not
* @profile: profile string
* @level: level string
* @other: other string information from codec
* @colorspace: colorspace identifier
* @xfer_func: transfer function identifier
* @ycbcr_enc: Y'CbCr encoding identifier
* @quantization: quantization identifier
*/
struct delta_streaminfo {
u32 flags;
u32 streamformat;
u32 width;
u32 height;
u32 dpb;
struct v4l2_rect crop;
struct v4l2_fract pixelaspect;
enum v4l2_field field;
u8 profile[32];
u8 level[32];
u8 other[32];
enum v4l2_colorspace colorspace;
enum v4l2_xfer_func xfer_func;
enum v4l2_ycbcr_encoding ycbcr_enc;
enum v4l2_quantization quantization;
};
#define DELTA_STREAMINFO_FLAG_CROP 0x0001
#define DELTA_STREAMINFO_FLAG_PIXELASPECT 0x0002
#define DELTA_STREAMINFO_FLAG_OTHER 0x0004
/*
* struct delta_au - access unit structure.
*
* @vbuf: video buffer information for V4L2
* @list: V4L2 m2m list that the frame belongs to
* @prepared: if set vaddr/paddr are resolved
* @vaddr: virtual address (kernel can read/write)
* @paddr: physical address (for hardware)
* @flags: access unit type (V4L2_BUF_FLAG_KEYFRAME/PFRAME/BFRAME)
* @dts: decoding timestamp of this access unit
*/
struct delta_au {
struct vb2_v4l2_buffer vbuf; /* keep first */
struct list_head list; /* keep second */
bool prepared;
u32 size;
void *vaddr;
dma_addr_t paddr;
u32 flags;
u64 dts;
};
/*
* struct delta_frameinfo - information about decoded frame
*
* @flags: validity of fields (crop, pixelaspect)
* @pixelformat: fourcc code for uncompressed video format
* @width: width of frame
* @height: height of frame
* @aligned_width: width of frame (with encoder or decoder alignment
* constraint)
* @aligned_height: height of frame (with encoder or decoder alignment
* constraint)
* @size: maximum size in bytes required for data
* @crop: cropping window inside frame (1920x1080@0,0
* inside 1920x1088 frame for ex.)
* @pixelaspect: pixel aspect ratio of video (4/3, 5/4)
* @field: interlaced mode
* @colorspace: colorspace identifier
* @xfer_func: transfer function identifier
* @ycbcr_enc: Y'CbCr encoding identifier
* @quantization: quantization identifier
*/
struct delta_frameinfo {
u32 flags;
u32 pixelformat;
u32 width;
u32 height;
u32 aligned_width;
u32 aligned_height;
u32 size;
struct v4l2_rect crop;
struct v4l2_fract pixelaspect;
enum v4l2_field field;
enum v4l2_colorspace colorspace;
enum v4l2_xfer_func xfer_func;
enum v4l2_ycbcr_encoding ycbcr_enc;
enum v4l2_quantization quantization;
};
#define DELTA_FRAMEINFO_FLAG_CROP 0x0001
#define DELTA_FRAMEINFO_FLAG_PIXELASPECT 0x0002
/*
* struct delta_frame - frame structure.
*
* @vbuf: video buffer information for V4L2
* @list: V4L2 m2m list that the frame belongs to
* @info: frame information (width, height, format, alignment...)
* @prepared: if set pix/vaddr/paddr are resolved
* @index: frame index, aligned on V4L2 wow
* @vaddr: virtual address (kernel can read/write)
* @paddr: physical address (for hardware)
* @state: frame state for frame lifecycle tracking
* (DELTA_FRAME_FREE/DEC/OUT/REC/...)
* @flags: frame type (V4L2_BUF_FLAG_KEYFRAME/PFRAME/BFRAME)
* @dts: decoding timestamp of this frame
* @field: field order for interlaced frame
*/
struct delta_frame {
struct vb2_v4l2_buffer vbuf; /* keep first */
struct list_head list; /* keep second */
struct delta_frameinfo info;
bool prepared;
u32 index;
void *vaddr;
dma_addr_t paddr;
u32 state;
u32 flags;
u64 dts;
enum v4l2_field field;
};
/* frame state for frame lifecycle tracking */
#define DELTA_FRAME_FREE 0x00 /* is free and can be used for decoding */
#define DELTA_FRAME_REF 0x01 /* is a reference frame */
#define DELTA_FRAME_BSY 0x02 /* is owned by decoder and busy */
#define DELTA_FRAME_DEC 0x04 /* contains decoded content */
#define DELTA_FRAME_OUT 0x08 /* has been given to user */
#define DELTA_FRAME_RDY 0x10 /* is ready but still held by decoder */
#define DELTA_FRAME_M2M 0x20 /* is owned by mem2mem framework */
/*
* struct delta_dts - decoding timestamp.
*
* @list: list to chain timestamps
* @val: timestamp in microseconds
*/
struct delta_dts {
struct list_head list;
u64 val;
};
struct delta_buf {
u32 size;
void *vaddr;
dma_addr_t paddr;
const char *name;
unsigned long attrs;
};
struct delta_ipc_ctx {
int cb_err;
u32 copro_hdl;
struct completion done;
struct delta_buf ipc_buf_struct;
struct delta_buf *ipc_buf;
};
struct delta_ipc_param {
u32 size;
void *data;
};
struct delta_ctx;
/*
* struct delta_dec - decoder structure.
*
* @name: name of this decoder
* @streamformat: input stream format that this decoder support
* @pixelformat: pixel format of decoded frame that this decoder support
* @max_width: (optional) maximum width that can decode this decoder
* if not set, maximum width is DELTA_MAX_WIDTH
* @max_height: (optional) maximum height that can decode this decoder
* if not set, maximum height is DELTA_MAX_HEIGHT
* @pm: (optional) if set, decoder will manage power on its own
* @open: open this decoder
* @close: close this decoder
* @setup_frame: setup frame to be used by decoder, see below
* @get_streaminfo: get stream related infos, see below
* @get_frameinfo: get decoded frame related infos, see below
* @set_frameinfo: (optional) set decoded frame related infos, see below
* @setup_frame: setup frame to be used by decoder, see below
* @decode: decode a single access unit, see below
* @get_frame: get the next decoded frame available, see below
* @recycle: recycle the given frame, see below
* @flush: (optional) flush decoder, see below
* @drain: (optional) drain decoder, see below
*/
struct delta_dec {
const char *name;
u32 streamformat;
u32 pixelformat;
u32 max_width;
u32 max_height;
bool pm;
/*
* decoder ops
*/
int (*open)(struct delta_ctx *ctx);
int (*close)(struct delta_ctx *ctx);
/*
* setup_frame() - setup frame to be used by decoder
* @ctx: (in) instance
* @frame: (in) frame to use
* @frame.index (in) identifier of frame
* @frame.vaddr (in) virtual address (kernel can read/write)
* @frame.paddr (in) physical address (for hardware)
*
* Frame is to be allocated by caller, then given
* to decoder through this call.
* Several frames must be given to decoder (dpb),
* each frame is identified using its index.
*/
int (*setup_frame)(struct delta_ctx *ctx, struct delta_frame *frame);
/*
* get_streaminfo() - get stream related infos
* @ctx: (in) instance
* @streaminfo: (out) width, height, dpb,...
*
* Precondition: stream header must have been successfully
* parsed to have this call successful & @streaminfo valid.
* Header parsing must be done using decode(), giving
* explicitly header access unit or first access unit of bitstream.
* If no valid header is found, get_streaminfo will return -ENODATA,
* in this case the next bitstream access unit must be decoded till
* get_streaminfo becomes successful.
*/
int (*get_streaminfo)(struct delta_ctx *ctx,
struct delta_streaminfo *streaminfo);
/*
* get_frameinfo() - get decoded frame related infos
* @ctx: (in) instance
* @frameinfo: (out) width, height, alignment, crop, ...
*
* Precondition: get_streaminfo() must be successful
*/
int (*get_frameinfo)(struct delta_ctx *ctx,
struct delta_frameinfo *frameinfo);
/*
* set_frameinfo() - set decoded frame related infos
* @ctx: (in) instance
* @frameinfo: (out) width, height, alignment, crop, ...
*
* Optional.
* Typically used to negotiate with decoder the output
* frame if decoder can do post-processing.
*/
int (*set_frameinfo)(struct delta_ctx *ctx,
struct delta_frameinfo *frameinfo);
/*
* decode() - decode a single access unit
* @ctx: (in) instance
* @au: (in/out) access unit
* @au.size (in) size of au to decode
* @au.vaddr (in) virtual address (kernel can read/write)
* @au.paddr (in) physical address (for hardware)
* @au.flags (out) au type (V4L2_BUF_FLAG_KEYFRAME/
* PFRAME/BFRAME)
*
* Decode the access unit given. Decode is synchronous;
* access unit memory is no more needed after this call.
* After this call, none, one or several frames could
* have been decoded, which can be retrieved using
* get_frame().
*/
int (*decode)(struct delta_ctx *ctx, struct delta_au *au);
/*
* get_frame() - get the next decoded frame available
* @ctx: (in) instance
* @frame: (out) frame with decoded data:
* @frame.index (out) identifier of frame
* @frame.field (out) field order for interlaced frame
* @frame.state (out) frame state for frame lifecycle tracking
* @frame.flags (out) frame type (V4L2_BUF_FLAG_KEYFRAME/
* PFRAME/BFRAME)
*
* Get the next available decoded frame.
* If no frame is available, -ENODATA is returned.
* If a frame is available, frame structure is filled with
* relevant data, frame.index identifying this exact frame.
* When this frame is no more needed by upper layers,
* recycle() must be called giving this frame identifier.
*/
int (*get_frame)(struct delta_ctx *ctx, struct delta_frame **frame);
/*
* recycle() - recycle the given frame
* @ctx: (in) instance
* @frame: (in) frame to recycle:
* @frame.index (in) identifier of frame
*
* recycle() is to be called by user when the decoded frame
* is no more needed (composition/display done).
* This frame will then be reused by decoder to proceed
* with next frame decoding.
* If not enough frames have been provided through setup_frame(),
* or recycle() is not called fast enough, the decoder can run out
* of available frames to proceed with decoding (starvation).
* This case is guarded by wq_recycle wait queue which ensures that
* decoder is called only if at least one frame is available.
*/
int (*recycle)(struct delta_ctx *ctx, struct delta_frame *frame);
/*
* flush() - flush decoder
* @ctx: (in) instance
*
* Optional.
* Reset decoder context and discard all internal buffers.
* This allows implementation of seek, which leads to discontinuity
* of input bitstream that decoder must know to restart its internal
* decoding logic.
*/
int (*flush)(struct delta_ctx *ctx);
/*
* drain() - drain decoder
* @ctx: (in) instance
*
* Optional.
* Mark decoder pending frames (decoded but not yet output) as ready
* so that they can be output to client at EOS (End Of Stream).
* get_frame() is to be called in a loop right after drain() to
* get all those pending frames.
*/
int (*drain)(struct delta_ctx *ctx);
};
struct delta_dev;
/*
* struct delta_ctx - instance structure.
*
* @flags: validity of fields (streaminfo)
* @fh: V4L2 file handle
* @dev: device context
* @dec: selected decoder context for this instance
* @ipc_ctx: context of IPC communication with firmware
* @state: instance state
* @frame_num: frame number
* @au_num: access unit number
* @max_au_size: max size of an access unit
* @streaminfo: stream information (width, height, dpb, interlacing...)
* @frameinfo: frame information (width, height, format, alignment...)
* @nb_of_frames: number of frames available for decoding
* @frames: array of decoding frames to keep track of frame
* state and manage frame recycling
* @decoded_frames: nb of decoded frames from opening
* @output_frames: nb of output frames from opening
* @dropped_frames: nb of frames dropped (ie access unit not parsed
* or frame decoded but not output)
* @stream_errors: nb of stream errors (corrupted, not supported, ...)
* @decode_errors: nb of decode errors (firmware error)
* @sys_errors: nb of system errors (memory, ipc, ...)
* @dts: FIFO of decoding timestamp.
* output frames are timestamped with incoming access
* unit timestamps using this fifo.
* @name: string naming this instance (debug purpose)
* @run_work: decoding work
* @lock: lock for decoding work serialization
* @aborting: true if current job aborted
* @priv: private decoder context for this instance, allocated
* by decoder @open time.
*/
struct delta_ctx {
u32 flags;
struct v4l2_fh fh;
struct delta_dev *dev;
const struct delta_dec *dec;
struct delta_ipc_ctx ipc_ctx;
enum delta_state state;
u32 frame_num;
u32 au_num;
size_t max_au_size;
struct delta_streaminfo streaminfo;
struct delta_frameinfo frameinfo;
u32 nb_of_frames;
struct delta_frame *frames[DELTA_MAX_FRAMES];
u32 decoded_frames;
u32 output_frames;
u32 dropped_frames;
u32 stream_errors;
u32 decode_errors;
u32 sys_errors;
struct list_head dts;
char name[100];
struct work_struct run_work;
struct mutex lock;
bool aborting;
void *priv;
};
#define DELTA_FLAG_STREAMINFO 0x0001
#define DELTA_FLAG_FRAMEINFO 0x0002
#define DELTA_MAX_FORMATS DELTA_MAX_DECODERS
/*
* struct delta_dev - device struct, 1 per probe (so single one for
* all platform life)
*
* @v4l2_dev: v4l2 device
* @vdev: v4l2 video device
* @pdev: platform device
* @dev: device
* @m2m_dev: memory-to-memory V4L2 device
* @lock: device lock, for crit section & V4L2 ops serialization.
* @clk_delta: delta main clock
* @clk_st231: st231 coprocessor main clock
* @clk_flash_promip: flash promip clock
* @decoders: list of registered decoders
* @nb_of_decoders: nb of registered decoders
* @pixelformats: supported uncompressed video formats
* @nb_of_pixelformats: number of supported umcompressed video formats
* @streamformats: supported compressed video formats
* @nb_of_streamformats:number of supported compressed video formats
* @instance_id: rolling counter identifying an instance (debug purpose)
* @work_queue: decoding job work queue
* @rpmsg_driver: rpmsg IPC driver
* @rpmsg_device: rpmsg IPC device
*/
struct delta_dev {
struct v4l2_device v4l2_dev;
struct video_device *vdev;
struct platform_device *pdev;
struct device *dev;
struct v4l2_m2m_dev *m2m_dev;
struct mutex lock;
struct clk *clk_delta;
struct clk *clk_st231;
struct clk *clk_flash_promip;
const struct delta_dec *decoders[DELTA_MAX_DECODERS];
u32 nb_of_decoders;
u32 pixelformats[DELTA_MAX_FORMATS];
u32 nb_of_pixelformats;
u32 streamformats[DELTA_MAX_FORMATS];
u32 nb_of_streamformats;
u8 instance_id;
struct workqueue_struct *work_queue;
struct rpmsg_driver rpmsg_driver;
struct rpmsg_device *rpmsg_device;
};
static inline char *frame_type_str(u32 flags)
{
if (flags & V4L2_BUF_FLAG_KEYFRAME)
return "I";
if (flags & V4L2_BUF_FLAG_PFRAME)
return "P";
if (flags & V4L2_BUF_FLAG_BFRAME)
return "B";
if (flags & V4L2_BUF_FLAG_LAST)
return "EOS";
return "?";
}
static inline char *frame_field_str(enum v4l2_field field)
{
if (field == V4L2_FIELD_NONE)
return "-";
if (field == V4L2_FIELD_TOP)
return "T";
if (field == V4L2_FIELD_BOTTOM)
return "B";
if (field == V4L2_FIELD_INTERLACED)
return "I";
if (field == V4L2_FIELD_INTERLACED_TB)
return "TB";
if (field == V4L2_FIELD_INTERLACED_BT)
return "BT";
return "?";
}
static inline char *frame_state_str(u32 state, char *str, unsigned int len)
{
snprintf(str, len, "%s %s %s %s %s %s",
(state & DELTA_FRAME_REF) ? "ref" : " ",
(state & DELTA_FRAME_BSY) ? "bsy" : " ",
(state & DELTA_FRAME_DEC) ? "dec" : " ",
(state & DELTA_FRAME_OUT) ? "out" : " ",
(state & DELTA_FRAME_M2M) ? "m2m" : " ",
(state & DELTA_FRAME_RDY) ? "rdy" : " ");
return str;
}
int delta_get_frameinfo_default(struct delta_ctx *ctx,
struct delta_frameinfo *frameinfo);
int delta_recycle_default(struct delta_ctx *pctx,
struct delta_frame *frame);
int delta_get_free_frame(struct delta_ctx *ctx,
struct delta_frame **pframe);
int delta_get_sync(struct delta_ctx *ctx);
void delta_put_autosuspend(struct delta_ctx *ctx);
#endif /* DELTA_H */
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_STI_HVA
tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver"
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_STI || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format
video encoder of STMicroelectronics SoC, allowing hardware encoding of
raw uncompressed formats in various compressed video bitstreams format.
To compile this driver as a module, choose M here:
the module will be called st-hva.
config VIDEO_STI_HVA_DEBUGFS
bool "Export STMicroelectronics HVA internals in debugfs"
depends on VIDEO_STI_HVA
depends on DEBUG_FS
help
Select this to see information about the internal state and the last
operation of STMicroelectronics HVA multi-format video encoder in
debugfs.
Choose N unless you know you need this.
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_STI_HVA) += st-hva.o
st-hva-y := hva-v4l2.o hva-hw.o hva-mem.o hva-h264.o
st-hva-$(CONFIG_VIDEO_STI_HVA_DEBUGFS) += hva-debugfs.o
@@ -0,0 +1,396 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
*/
#include <linux/debugfs.h>
#include "hva.h"
#include "hva-hw.h"
static void format_ctx(struct seq_file *s, struct hva_ctx *ctx)
{
struct hva_streaminfo *stream = &ctx->streaminfo;
struct hva_frameinfo *frame = &ctx->frameinfo;
struct hva_controls *ctrls = &ctx->ctrls;
struct hva_ctx_dbg *dbg = &ctx->dbg;
u32 bitrate_mode, aspect, entropy, vui_sar, sei_fp;
seq_printf(s, "|-%s\n |\n", ctx->name);
seq_printf(s, " |-[%sframe info]\n",
ctx->flags & HVA_FLAG_FRAMEINFO ? "" : "default ");
seq_printf(s, " | |- pixel format=%4.4s\n"
" | |- wxh=%dx%d\n"
" | |- wxh (w/ encoder alignment constraint)=%dx%d\n"
" |\n",
(char *)&frame->pixelformat,
frame->width, frame->height,
frame->aligned_width, frame->aligned_height);
seq_printf(s, " |-[%sstream info]\n",
ctx->flags & HVA_FLAG_STREAMINFO ? "" : "default ");
seq_printf(s, " | |- stream format=%4.4s\n"
" | |- wxh=%dx%d\n"
" | |- %s\n"
" | |- %s\n"
" |\n",
(char *)&stream->streamformat,
stream->width, stream->height,
stream->profile, stream->level);
bitrate_mode = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
aspect = V4L2_CID_MPEG_VIDEO_ASPECT;
seq_puts(s, " |-[parameters]\n");
seq_printf(s, " | |- %s\n"
" | |- bitrate=%d bps\n"
" | |- GOP size=%d\n"
" | |- video aspect=%s\n"
" | |- framerate=%d/%d\n",
v4l2_ctrl_get_menu(bitrate_mode)[ctrls->bitrate_mode],
ctrls->bitrate,
ctrls->gop_size,
v4l2_ctrl_get_menu(aspect)[ctrls->aspect],
ctrls->time_per_frame.denominator,
ctrls->time_per_frame.numerator);
entropy = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE;
vui_sar = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC;
sei_fp = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE;
if (stream->streamformat == V4L2_PIX_FMT_H264) {
seq_printf(s, " | |- %s entropy mode\n"
" | |- CPB size=%d kB\n"
" | |- DCT8x8 enable=%s\n"
" | |- qpmin=%d\n"
" | |- qpmax=%d\n"
" | |- PAR enable=%s\n"
" | |- PAR id=%s\n"
" | |- SEI frame packing enable=%s\n"
" | |- SEI frame packing type=%s\n",
v4l2_ctrl_get_menu(entropy)[ctrls->entropy_mode],
ctrls->cpb_size,
ctrls->dct8x8 ? "true" : "false",
ctrls->qpmin,
ctrls->qpmax,
ctrls->vui_sar ? "true" : "false",
v4l2_ctrl_get_menu(vui_sar)[ctrls->vui_sar_idc],
ctrls->sei_fp ? "true" : "false",
v4l2_ctrl_get_menu(sei_fp)[ctrls->sei_fp_type]);
}
if (ctx->sys_errors || ctx->encode_errors || ctx->frame_errors) {
seq_puts(s, " |\n |-[errors]\n");
seq_printf(s, " | |- system=%d\n"
" | |- encoding=%d\n"
" | |- frame=%d\n",
ctx->sys_errors,
ctx->encode_errors,
ctx->frame_errors);
}
seq_puts(s, " |\n |-[performances]\n");
seq_printf(s, " | |- frames encoded=%d\n"
" | |- avg HW processing duration (0.1ms)=%d [min=%d, max=%d]\n"
" | |- avg encoding period (0.1ms)=%d [min=%d, max=%d]\n"
" | |- avg fps (0.1Hz)=%d\n"
" | |- max reachable fps (0.1Hz)=%d\n"
" | |- avg bitrate (kbps)=%d [min=%d, max=%d]\n"
" | |- last bitrate (kbps)=%d\n",
dbg->cnt_duration,
dbg->avg_duration,
dbg->min_duration,
dbg->max_duration,
dbg->avg_period,
dbg->min_period,
dbg->max_period,
dbg->avg_fps,
dbg->max_fps,
dbg->avg_bitrate,
dbg->min_bitrate,
dbg->max_bitrate,
dbg->last_bitrate);
}
/*
* performance debug info
*/
void hva_dbg_perf_begin(struct hva_ctx *ctx)
{
u64 div;
u32 period;
u32 bitrate;
struct hva_ctx_dbg *dbg = &ctx->dbg;
ktime_t prev = dbg->begin;
dbg->begin = ktime_get();
if (dbg->is_valid_period) {
/* encoding period */
div = (u64)ktime_us_delta(dbg->begin, prev);
do_div(div, 100);
period = (u32)div;
dbg->min_period = min(period, dbg->min_period);
dbg->max_period = max(period, dbg->max_period);
dbg->total_period += period;
dbg->cnt_period++;
/*
* minimum and maximum bitrates are based on the
* encoding period values upon a window of 32 samples
*/
dbg->window_duration += period;
dbg->cnt_window++;
if (dbg->cnt_window >= 32) {
/*
* bitrate in kbps = (size * 8 / 1000) /
* (duration / 10000)
* = size * 80 / duration
*/
if (dbg->window_duration > 0) {
div = (u64)dbg->window_stream_size * 80;
do_div(div, dbg->window_duration);
bitrate = (u32)div;
dbg->last_bitrate = bitrate;
dbg->min_bitrate = min(bitrate,
dbg->min_bitrate);
dbg->max_bitrate = max(bitrate,
dbg->max_bitrate);
}
dbg->window_stream_size = 0;
dbg->window_duration = 0;
dbg->cnt_window = 0;
}
}
/*
* filter sequences valid for performance:
* - begin/begin (no stream available) is an invalid sequence
* - begin/end is a valid sequence
*/
dbg->is_valid_period = false;
}
void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream)
{
struct device *dev = ctx_to_dev(ctx);
u64 div;
u32 duration;
u32 bytesused;
u32 timestamp;
struct hva_ctx_dbg *dbg = &ctx->dbg;
ktime_t end = ktime_get();
/* stream bytesused and timestamp in us */
bytesused = vb2_get_plane_payload(&stream->vbuf.vb2_buf, 0);
div = stream->vbuf.vb2_buf.timestamp;
do_div(div, 1000);
timestamp = (u32)div;
/* encoding duration */
div = (u64)ktime_us_delta(end, dbg->begin);
dev_dbg(dev,
"%s perf stream[%d] dts=%d encoded using %d bytes in %d us",
ctx->name,
stream->vbuf.sequence,
timestamp,
bytesused, (u32)div);
do_div(div, 100);
duration = (u32)div;
dbg->min_duration = min(duration, dbg->min_duration);
dbg->max_duration = max(duration, dbg->max_duration);
dbg->total_duration += duration;
dbg->cnt_duration++;
/*
* the average bitrate is based on the total stream size
* and the total encoding periods
*/
dbg->total_stream_size += bytesused;
dbg->window_stream_size += bytesused;
dbg->is_valid_period = true;
}
static void hva_dbg_perf_compute(struct hva_ctx *ctx)
{
u64 div;
struct hva_ctx_dbg *dbg = &ctx->dbg;
if (dbg->cnt_duration > 0) {
div = (u64)dbg->total_duration;
do_div(div, dbg->cnt_duration);
dbg->avg_duration = (u32)div;
} else {
dbg->avg_duration = 0;
}
if (dbg->total_duration > 0) {
div = (u64)dbg->cnt_duration * 100000;
do_div(div, dbg->total_duration);
dbg->max_fps = (u32)div;
} else {
dbg->max_fps = 0;
}
if (dbg->cnt_period > 0) {
div = (u64)dbg->total_period;
do_div(div, dbg->cnt_period);
dbg->avg_period = (u32)div;
} else {
dbg->avg_period = 0;
}
if (dbg->total_period > 0) {
div = (u64)dbg->cnt_period * 100000;
do_div(div, dbg->total_period);
dbg->avg_fps = (u32)div;
} else {
dbg->avg_fps = 0;
}
if (dbg->total_period > 0) {
/*
* bitrate in kbps = (video size * 8 / 1000) /
* (video duration / 10000)
* = video size * 80 / video duration
*/
div = (u64)dbg->total_stream_size * 80;
do_div(div, dbg->total_period);
dbg->avg_bitrate = (u32)div;
} else {
dbg->avg_bitrate = 0;
}
}
/*
* device debug info
*/
static int device_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
seq_printf(s, "[%s]\n", hva->v4l2_dev.name);
seq_printf(s, "registered as /dev/video%d\n", hva->vdev->num);
return 0;
}
static int encoders_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
unsigned int i = 0;
seq_printf(s, "[encoders]\n|- %d registered encoders:\n",
hva->nb_of_encoders);
while (hva->encoders[i]) {
seq_printf(s, "|- %s: %4.4s => %4.4s\n", hva->encoders[i]->name,
(char *)&hva->encoders[i]->pixelformat,
(char *)&hva->encoders[i]->streamformat);
i++;
}
return 0;
}
static int last_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
struct hva_ctx *last_ctx = &hva->dbg.last_ctx;
if (last_ctx->flags & HVA_FLAG_STREAMINFO) {
seq_puts(s, "[last encoding]\n");
hva_dbg_perf_compute(last_ctx);
format_ctx(s, last_ctx);
} else {
seq_puts(s, "[no information recorded about last encoding]\n");
}
return 0;
}
static int regs_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
hva_hw_dump_regs(hva, s);
return 0;
}
#define hva_dbg_create_entry(name) \
debugfs_create_file(#name, 0444, hva->dbg.debugfs_entry, hva, \
&name##_fops)
DEFINE_SHOW_ATTRIBUTE(device);
DEFINE_SHOW_ATTRIBUTE(encoders);
DEFINE_SHOW_ATTRIBUTE(last);
DEFINE_SHOW_ATTRIBUTE(regs);
void hva_debugfs_create(struct hva_dev *hva)
{
hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL);
hva_dbg_create_entry(device);
hva_dbg_create_entry(encoders);
hva_dbg_create_entry(last);
hva_dbg_create_entry(regs);
}
void hva_debugfs_remove(struct hva_dev *hva)
{
debugfs_remove_recursive(hva->dbg.debugfs_entry);
hva->dbg.debugfs_entry = NULL;
}
/*
* context (instance) debug info
*/
static int ctx_show(struct seq_file *s, void *data)
{
struct hva_ctx *ctx = s->private;
seq_printf(s, "[running encoding %d]\n", ctx->id);
hva_dbg_perf_compute(ctx);
format_ctx(s, ctx);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ctx);
void hva_dbg_ctx_create(struct hva_ctx *ctx)
{
struct hva_dev *hva = ctx->hva_dev;
char name[4] = "";
ctx->dbg.min_duration = UINT_MAX;
ctx->dbg.min_period = UINT_MAX;
ctx->dbg.min_bitrate = UINT_MAX;
snprintf(name, sizeof(name), "%d", hva->instance_id);
ctx->dbg.debugfs_entry = debugfs_create_file(name, 0444,
hva->dbg.debugfs_entry,
ctx, &ctx_fops);
}
void hva_dbg_ctx_remove(struct hva_ctx *ctx)
{
struct hva_dev *hva = ctx->hva_dev;
if (ctx->flags & HVA_FLAG_STREAMINFO)
/* save context before removing */
memcpy(&hva->dbg.last_ctx, ctx, sizeof(*ctx));
debugfs_remove(ctx->dbg.debugfs_entry);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,584 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
#include <linux/seq_file.h>
#endif
#include "hva.h"
#include "hva-hw.h"
/* HVA register offsets */
#define HVA_HIF_REG_RST 0x0100U
#define HVA_HIF_REG_RST_ACK 0x0104U
#define HVA_HIF_REG_MIF_CFG 0x0108U
#define HVA_HIF_REG_HEC_MIF_CFG 0x010CU
#define HVA_HIF_REG_CFL 0x0110U
#define HVA_HIF_FIFO_CMD 0x0114U
#define HVA_HIF_FIFO_STS 0x0118U
#define HVA_HIF_REG_SFL 0x011CU
#define HVA_HIF_REG_IT_ACK 0x0120U
#define HVA_HIF_REG_ERR_IT_ACK 0x0124U
#define HVA_HIF_REG_LMI_ERR 0x0128U
#define HVA_HIF_REG_EMI_ERR 0x012CU
#define HVA_HIF_REG_HEC_MIF_ERR 0x0130U
#define HVA_HIF_REG_HEC_STS 0x0134U
#define HVA_HIF_REG_HVC_STS 0x0138U
#define HVA_HIF_REG_HJE_STS 0x013CU
#define HVA_HIF_REG_CNT 0x0140U
#define HVA_HIF_REG_HEC_CHKSYN_DIS 0x0144U
#define HVA_HIF_REG_CLK_GATING 0x0148U
#define HVA_HIF_REG_VERSION 0x014CU
#define HVA_HIF_REG_BSM 0x0150U
/* define value for version id register (HVA_HIF_REG_VERSION) */
#define VERSION_ID_MASK 0x0000FFFF
/* define values for BSM register (HVA_HIF_REG_BSM) */
#define BSM_CFG_VAL1 0x0003F000
#define BSM_CFG_VAL2 0x003F0000
/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */
#define MIF_CFG_VAL1 0x04460446
#define MIF_CFG_VAL2 0x04460806
#define MIF_CFG_VAL3 0x00000000
/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */
#define HEC_MIF_CFG_VAL 0x000000C4
/* Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */
#define CLK_GATING_HVC BIT(0)
#define CLK_GATING_HEC BIT(1)
#define CLK_GATING_HJE BIT(2)
/* fix hva clock rate */
#define CLK_RATE 300000000
/* fix delay for pmruntime */
#define AUTOSUSPEND_DELAY_MS 3
/*
* hw encode error values
* NO_ERROR: Success, Task OK
* H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer
* H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size)
* H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size
* H264_MAX_SLICE_NUMBER: VECH264 max slice number reached
* H264_SLICE_READY: VECH264 Slice ready
* TASK_LIST_FULL: HVA/FPC task list full
(discard latest transform command)
* UNKNOWN_COMMAND: Transform command not known by HVA/FPC
* WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection
* NO_INT_COMPLETION: Time-out on interrupt completion
* LMI_ERR: Local Memory Interface Error
* EMI_ERR: External Memory Interface Error
* HECMI_ERR: HEC Memory Interface Error
*/
enum hva_hw_error {
NO_ERROR = 0x0,
H264_BITSTREAM_OVERSIZE = 0x2,
H264_FRAME_SKIPPED = 0x4,
H264_SLICE_LIMIT_SIZE = 0x5,
H264_MAX_SLICE_NUMBER = 0x7,
H264_SLICE_READY = 0x8,
TASK_LIST_FULL = 0xF0,
UNKNOWN_COMMAND = 0xF1,
WRONG_CODEC_OR_RESOLUTION = 0xF4,
NO_INT_COMPLETION = 0x100,
LMI_ERR = 0x101,
EMI_ERR = 0x102,
HECMI_ERR = 0x103,
};
static irqreturn_t hva_hw_its_interrupt(int irq, void *data)
{
struct hva_dev *hva = data;
/* read status registers */
hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
/* acknowledge interruption */
writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
return IRQ_WAKE_THREAD;
}
static irqreturn_t hva_hw_its_irq_thread(int irq, void *arg)
{
struct hva_dev *hva = arg;
struct device *dev = hva_to_dev(hva);
u32 status = hva->sts_reg & 0xFF;
u8 ctx_id = 0;
struct hva_ctx *ctx = NULL;
dev_dbg(dev, "%s %s: status: 0x%02x fifo level: 0x%02x\n",
HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
/*
* status: task_id[31:16] client_id[15:8] status[7:0]
* the context identifier is retrieved from the client identifier
*/
ctx_id = (hva->sts_reg & 0xFF00) >> 8;
if (ctx_id >= HVA_MAX_INSTANCES) {
dev_err(dev, "%s %s: bad context identifier: %d\n",
HVA_PREFIX, __func__, ctx_id);
goto out;
}
ctx = hva->instances[ctx_id];
if (!ctx)
goto out;
switch (status) {
case NO_ERROR:
dev_dbg(dev, "%s %s: no error\n",
ctx->name, __func__);
ctx->hw_err = false;
break;
case H264_SLICE_READY:
dev_dbg(dev, "%s %s: h264 slice ready\n",
ctx->name, __func__);
ctx->hw_err = false;
break;
case H264_FRAME_SKIPPED:
dev_dbg(dev, "%s %s: h264 frame skipped\n",
ctx->name, __func__);
ctx->hw_err = false;
break;
case H264_BITSTREAM_OVERSIZE:
dev_err(dev, "%s %s:h264 bitstream oversize\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
case H264_SLICE_LIMIT_SIZE:
dev_err(dev, "%s %s: h264 slice limit size is reached\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
case H264_MAX_SLICE_NUMBER:
dev_err(dev, "%s %s: h264 max slice number is reached\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
case TASK_LIST_FULL:
dev_err(dev, "%s %s:task list full\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
case UNKNOWN_COMMAND:
dev_err(dev, "%s %s: command not known\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
case WRONG_CODEC_OR_RESOLUTION:
dev_err(dev, "%s %s: wrong codec or resolution\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
default:
dev_err(dev, "%s %s: status not recognized\n",
ctx->name, __func__);
ctx->hw_err = true;
break;
}
out:
complete(&hva->interrupt);
return IRQ_HANDLED;
}
static irqreturn_t hva_hw_err_interrupt(int irq, void *data)
{
struct hva_dev *hva = data;
/* read status registers */
hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
/* read error registers */
hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR);
hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR);
hva->hec_mif_err_reg = readl_relaxed(hva->regs +
HVA_HIF_REG_HEC_MIF_ERR);
/* acknowledge interruption */
writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
return IRQ_WAKE_THREAD;
}
static irqreturn_t hva_hw_err_irq_thread(int irq, void *arg)
{
struct hva_dev *hva = arg;
struct device *dev = hva_to_dev(hva);
u8 ctx_id = 0;
struct hva_ctx *ctx;
dev_dbg(dev, "%s status: 0x%02x fifo level: 0x%02x\n",
HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
/*
* status: task_id[31:16] client_id[15:8] status[7:0]
* the context identifier is retrieved from the client identifier
*/
ctx_id = (hva->sts_reg & 0xFF00) >> 8;
if (ctx_id >= HVA_MAX_INSTANCES) {
dev_err(dev, "%s bad context identifier: %d\n", HVA_PREFIX,
ctx_id);
goto out;
}
ctx = hva->instances[ctx_id];
if (!ctx)
goto out;
if (hva->lmi_err_reg) {
dev_err(dev, "%s local memory interface error: 0x%08x\n",
ctx->name, hva->lmi_err_reg);
ctx->hw_err = true;
}
if (hva->emi_err_reg) {
dev_err(dev, "%s external memory interface error: 0x%08x\n",
ctx->name, hva->emi_err_reg);
ctx->hw_err = true;
}
if (hva->hec_mif_err_reg) {
dev_err(dev, "%s hec memory interface error: 0x%08x\n",
ctx->name, hva->hec_mif_err_reg);
ctx->hw_err = true;
}
out:
complete(&hva->interrupt);
return IRQ_HANDLED;
}
static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva)
{
struct device *dev = hva_to_dev(hva);
unsigned long int version;
if (pm_runtime_resume_and_get(dev) < 0) {
dev_err(dev, "%s failed to get pm_runtime\n", HVA_PREFIX);
mutex_unlock(&hva->protect_mutex);
return -EFAULT;
}
version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
VERSION_ID_MASK;
pm_runtime_put_autosuspend(dev);
switch (version) {
case HVA_VERSION_V400:
dev_dbg(dev, "%s IP hardware version 0x%lx\n",
HVA_PREFIX, version);
break;
default:
dev_err(dev, "%s unknown IP hardware version 0x%lx\n",
HVA_PREFIX, version);
version = HVA_VERSION_UNKNOWN;
break;
}
return version;
}
int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva)
{
struct device *dev = &pdev->dev;
struct resource *esram;
int ret;
WARN_ON(!hva);
/* get memory for registers */
hva->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hva->regs)) {
dev_err(dev, "%s failed to get regs\n", HVA_PREFIX);
return PTR_ERR(hva->regs);
}
/* get memory for esram */
esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!esram) {
dev_err(dev, "%s failed to get esram\n", HVA_PREFIX);
return -ENODEV;
}
hva->esram_addr = esram->start;
hva->esram_size = resource_size(esram);
dev_info(dev, "%s esram reserved for address: 0x%x size:%d\n",
HVA_PREFIX, hva->esram_addr, hva->esram_size);
/* get clock resource */
hva->clk = devm_clk_get(dev, "clk_hva");
if (IS_ERR(hva->clk)) {
dev_err(dev, "%s failed to get clock\n", HVA_PREFIX);
return PTR_ERR(hva->clk);
}
ret = clk_prepare(hva->clk);
if (ret < 0) {
dev_err(dev, "%s failed to prepare clock\n", HVA_PREFIX);
hva->clk = ERR_PTR(-EINVAL);
return ret;
}
/* get status interruption resource */
ret = platform_get_irq(pdev, 0);
if (ret < 0)
goto err_clk;
hva->irq_its = ret;
ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt,
hva_hw_its_irq_thread,
IRQF_ONESHOT,
"hva_its_irq", hva);
if (ret) {
dev_err(dev, "%s failed to install status IRQ 0x%x\n",
HVA_PREFIX, hva->irq_its);
goto err_clk;
}
disable_irq(hva->irq_its);
/* get error interruption resource */
ret = platform_get_irq(pdev, 1);
if (ret < 0)
goto err_clk;
hva->irq_err = ret;
ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt,
hva_hw_err_irq_thread,
IRQF_ONESHOT,
"hva_err_irq", hva);
if (ret) {
dev_err(dev, "%s failed to install error IRQ 0x%x\n",
HVA_PREFIX, hva->irq_err);
goto err_clk;
}
disable_irq(hva->irq_err);
/* initialise protection mutex */
mutex_init(&hva->protect_mutex);
/* initialise completion signal */
init_completion(&hva->interrupt);
/* initialise runtime power management */
pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err(dev, "%s failed to set PM\n", HVA_PREFIX);
goto err_disable;
}
/* check IP hardware version */
hva->ip_version = hva_hw_get_ip_version(hva);
if (hva->ip_version == HVA_VERSION_UNKNOWN) {
ret = -EINVAL;
goto err_pm;
}
dev_info(dev, "%s found hva device (version 0x%lx)\n", HVA_PREFIX,
hva->ip_version);
return 0;
err_pm:
pm_runtime_put(dev);
err_disable:
pm_runtime_disable(dev);
err_clk:
clk_unprepare(hva->clk);
return ret;
}
void hva_hw_remove(struct hva_dev *hva)
{
struct device *dev = hva_to_dev(hva);
disable_irq(hva->irq_its);
disable_irq(hva->irq_err);
pm_runtime_put_autosuspend(dev);
pm_runtime_disable(dev);
}
int hva_hw_runtime_suspend(struct device *dev)
{
struct hva_dev *hva = dev_get_drvdata(dev);
clk_disable_unprepare(hva->clk);
return 0;
}
int hva_hw_runtime_resume(struct device *dev)
{
struct hva_dev *hva = dev_get_drvdata(dev);
if (clk_prepare_enable(hva->clk)) {
dev_err(hva->dev, "%s failed to prepare hva clk\n",
HVA_PREFIX);
return -EINVAL;
}
if (clk_set_rate(hva->clk, CLK_RATE)) {
dev_err(dev, "%s failed to set clock frequency\n",
HVA_PREFIX);
clk_disable_unprepare(hva->clk);
return -EINVAL;
}
return 0;
}
int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
struct hva_buffer *task)
{
struct hva_dev *hva = ctx_to_hdev(ctx);
struct device *dev = hva_to_dev(hva);
u8 client_id = ctx->id;
int ret;
u32 reg = 0;
bool got_pm = false;
mutex_lock(&hva->protect_mutex);
/* enable irqs */
enable_irq(hva->irq_its);
enable_irq(hva->irq_err);
if (pm_runtime_resume_and_get(dev) < 0) {
dev_err(dev, "%s failed to get pm_runtime\n", ctx->name);
ctx->sys_errors++;
ret = -EFAULT;
goto out;
}
got_pm = true;
reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING);
switch (cmd) {
case H264_ENC:
reg |= CLK_GATING_HVC;
break;
default:
dev_dbg(dev, "%s unknown command 0x%x\n", ctx->name, cmd);
ctx->encode_errors++;
ret = -EFAULT;
goto out;
}
writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
dev_dbg(dev, "%s %s: write configuration registers\n", ctx->name,
__func__);
/* byte swap config */
writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM);
/* define Max Opcode Size and Max Message Size for LMI and EMI */
writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG);
writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG);
/*
* command FIFO: task_id[31:16] client_id[15:8] command_type[7:0]
* the context identifier is provided as client identifier to the
* hardware, and is retrieved in the interrupt functions from the
* status register
*/
dev_dbg(dev, "%s %s: send task (cmd: %d, task_desc: %pad)\n",
ctx->name, __func__, cmd + (client_id << 8), &task->paddr);
writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD);
writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD);
if (!wait_for_completion_timeout(&hva->interrupt,
msecs_to_jiffies(2000))) {
dev_err(dev, "%s %s: time out on completion\n", ctx->name,
__func__);
ctx->encode_errors++;
ret = -EFAULT;
goto out;
}
/* get encoding status */
ret = ctx->hw_err ? -EFAULT : 0;
ctx->encode_errors += ctx->hw_err ? 1 : 0;
out:
disable_irq(hva->irq_its);
disable_irq(hva->irq_err);
switch (cmd) {
case H264_ENC:
reg &= ~CLK_GATING_HVC;
writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
break;
default:
dev_dbg(dev, "%s unknown command 0x%x\n", ctx->name, cmd);
}
if (got_pm)
pm_runtime_put_autosuspend(dev);
mutex_unlock(&hva->protect_mutex);
return ret;
}
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
#define DUMP(reg) seq_printf(s, "%-30s: 0x%08X\n",\
#reg, readl_relaxed(hva->regs + reg))
void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s)
{
struct device *dev = hva_to_dev(hva);
mutex_lock(&hva->protect_mutex);
if (pm_runtime_resume_and_get(dev) < 0) {
seq_puts(s, "Cannot wake up IP\n");
mutex_unlock(&hva->protect_mutex);
return;
}
seq_printf(s, "Registers:\nReg @ = 0x%p\n", hva->regs);
DUMP(HVA_HIF_REG_RST);
DUMP(HVA_HIF_REG_RST_ACK);
DUMP(HVA_HIF_REG_MIF_CFG);
DUMP(HVA_HIF_REG_HEC_MIF_CFG);
DUMP(HVA_HIF_REG_CFL);
DUMP(HVA_HIF_REG_SFL);
DUMP(HVA_HIF_REG_LMI_ERR);
DUMP(HVA_HIF_REG_EMI_ERR);
DUMP(HVA_HIF_REG_HEC_MIF_ERR);
DUMP(HVA_HIF_REG_HEC_STS);
DUMP(HVA_HIF_REG_HVC_STS);
DUMP(HVA_HIF_REG_HJE_STS);
DUMP(HVA_HIF_REG_CNT);
DUMP(HVA_HIF_REG_HEC_CHKSYN_DIS);
DUMP(HVA_HIF_REG_CLK_GATING);
DUMP(HVA_HIF_REG_VERSION);
pm_runtime_put_autosuspend(dev);
mutex_unlock(&hva->protect_mutex);
}
#endif
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
*/
#ifndef HVA_HW_H
#define HVA_HW_H
#include "hva-mem.h"
/* HVA Versions */
#define HVA_VERSION_UNKNOWN 0x000
#define HVA_VERSION_V400 0x400
/* HVA command types */
enum hva_hw_cmd_type {
/* RESERVED = 0x00 */
/* RESERVED = 0x01 */
H264_ENC = 0x02,
/* RESERVED = 0x03 */
/* RESERVED = 0x04 */
/* RESERVED = 0x05 */
/* RESERVED = 0x06 */
/* RESERVED = 0x07 */
REMOVE_CLIENT = 0x08,
FREEZE_CLIENT = 0x09,
START_CLIENT = 0x0A,
FREEZE_ALL = 0x0B,
START_ALL = 0x0C,
REMOVE_ALL = 0x0D
};
int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva);
void hva_hw_remove(struct hva_dev *hva);
int hva_hw_runtime_suspend(struct device *dev);
int hva_hw_runtime_resume(struct device *dev);
int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
struct hva_buffer *task);
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s);
#endif
#endif /* HVA_HW_H */
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
*/
#include "hva.h"
#include "hva-mem.h"
int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name,
struct hva_buffer **buf)
{
struct device *dev = ctx_to_dev(ctx);
struct hva_buffer *b;
dma_addr_t paddr;
void *base;
b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
if (!b) {
ctx->sys_errors++;
return -ENOMEM;
}
base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL,
DMA_ATTR_WRITE_COMBINE);
if (!base) {
dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n",
ctx->name, __func__, name, size);
ctx->sys_errors++;
devm_kfree(dev, b);
return -ENOMEM;
}
b->size = size;
b->paddr = paddr;
b->vaddr = base;
b->name = name;
dev_dbg(dev,
"%s allocate %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
ctx->name, size, b->vaddr, &b->paddr, b->name);
/* return hva buffer to user */
*buf = b;
return 0;
}
void hva_mem_free(struct hva_ctx *ctx, struct hva_buffer *buf)
{
struct device *dev = ctx_to_dev(ctx);
dev_dbg(dev,
"%s free %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name);
dma_free_attrs(dev, buf->size, buf->vaddr, buf->paddr,
DMA_ATTR_WRITE_COMBINE);
devm_kfree(dev, buf);
}
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
*/
#ifndef HVA_MEM_H
#define HVA_MEM_H
/**
* struct hva_buffer - hva buffer
*
* @name: name of requester
* @paddr: physical address (for hardware)
* @vaddr: virtual address (kernel can read/write)
* @size: size of buffer
*/
struct hva_buffer {
const char *name;
dma_addr_t paddr;
void *vaddr;
u32 size;
};
int hva_mem_alloc(struct hva_ctx *ctx,
__u32 size,
const char *name,
struct hva_buffer **buf);
void hva_mem_free(struct hva_ctx *ctx,
struct hva_buffer *buf);
#endif /* HVA_MEM_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,407 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
*/
#ifndef HVA_H
#define HVA_H
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-mem2mem.h>
#define hva_to_dev(h) (h->dev)
#define ctx_to_dev(c) (c->hva_dev->dev)
#define ctx_to_hdev(c) (c->hva_dev)
#define HVA_NAME "st-hva"
#define HVA_PREFIX "[---:----]"
extern const struct hva_enc nv12h264enc;
extern const struct hva_enc nv21h264enc;
/**
* struct hva_frameinfo - information about hva frame
*
* @pixelformat: fourcc code for uncompressed video format
* @width: width of frame
* @height: height of frame
* @aligned_width: width of frame (with encoder alignment constraint)
* @aligned_height: height of frame (with encoder alignment constraint)
* @size: maximum size in bytes required for data
*/
struct hva_frameinfo {
u32 pixelformat;
u32 width;
u32 height;
u32 aligned_width;
u32 aligned_height;
u32 size;
};
/**
* struct hva_streaminfo - information about hva stream
*
* @streamformat: fourcc code of compressed video format (H.264...)
* @width: width of stream
* @height: height of stream
* @profile: profile string
* @level: level string
*/
struct hva_streaminfo {
u32 streamformat;
u32 width;
u32 height;
u8 profile[32];
u8 level[32];
};
/**
* struct hva_controls - hva controls set
*
* @time_per_frame: time per frame in seconds
* @bitrate_mode: bitrate mode (constant bitrate or variable bitrate)
* @gop_size: groupe of picture size
* @bitrate: bitrate (in bps)
* @aspect: video aspect
* @profile: H.264 profile
* @level: H.264 level
* @entropy_mode: H.264 entropy mode (CABAC or CVLC)
* @cpb_size: coded picture buffer size (in kB)
* @dct8x8: transform mode 8x8 enable
* @qpmin: minimum quantizer
* @qpmax: maximum quantizer
* @vui_sar: pixel aspect ratio enable
* @vui_sar_idc: pixel aspect ratio identifier
* @sei_fp: sei frame packing arrangement enable
* @sei_fp_type: sei frame packing arrangement type
*/
struct hva_controls {
struct v4l2_fract time_per_frame;
enum v4l2_mpeg_video_bitrate_mode bitrate_mode;
u32 gop_size;
u32 bitrate;
enum v4l2_mpeg_video_aspect aspect;
enum v4l2_mpeg_video_h264_profile profile;
enum v4l2_mpeg_video_h264_level level;
enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
u32 cpb_size;
bool dct8x8;
u32 qpmin;
u32 qpmax;
bool vui_sar;
enum v4l2_mpeg_video_h264_vui_sar_idc vui_sar_idc;
bool sei_fp;
enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_type;
};
/**
* struct hva_frame - hva frame buffer (output)
*
* @vbuf: video buffer information for V4L2
* @list: V4L2 m2m list that the frame belongs to
* @info: frame information (width, height, format, alignment...)
* @paddr: physical address (for hardware)
* @vaddr: virtual address (kernel can read/write)
* @prepared: true if vaddr/paddr are resolved
*/
struct hva_frame {
struct vb2_v4l2_buffer vbuf;
struct list_head list;
struct hva_frameinfo info;
dma_addr_t paddr;
void *vaddr;
bool prepared;
};
/*
* to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame *
*/
#define to_hva_frame(vb) \
container_of(vb, struct hva_frame, vbuf)
/**
* struct hva_stream - hva stream buffer (capture)
*
* @vbuf: video buffer information for V4L2
* @list: V4L2 m2m list that the frame belongs to
* @paddr: physical address (for hardware)
* @vaddr: virtual address (kernel can read/write)
* @prepared: true if vaddr/paddr are resolved
* @size: size of the buffer in bytes
* @bytesused: number of bytes occupied by data in the buffer
*/
struct hva_stream {
struct vb2_v4l2_buffer vbuf;
struct list_head list;
dma_addr_t paddr;
void *vaddr;
bool prepared;
unsigned int size;
unsigned int bytesused;
};
/*
* to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream *
*/
#define to_hva_stream(vb) \
container_of(vb, struct hva_stream, vbuf)
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
/**
* struct hva_ctx_dbg - instance context debug info
*
* @debugfs_entry: debugfs entry
* @is_valid_period: true if the sequence is valid for performance
* @begin: start time of last HW task
* @total_duration: total HW processing durations in 0.1ms
* @cnt_duration: number of HW processings
* @min_duration: minimum HW processing duration in 0.1ms
* @max_duration: maximum HW processing duration in 0.1ms
* @avg_duration: average HW processing duration in 0.1ms
* @max_fps: maximum frames encoded per second (in 0.1Hz)
* @total_period: total encoding periods in 0.1ms
* @cnt_period: number of periods
* @min_period: minimum encoding period in 0.1ms
* @max_period: maximum encoding period in 0.1ms
* @avg_period: average encoding period in 0.1ms
* @total_stream_size: total number of encoded bytes
* @avg_fps: average frames encoded per second (in 0.1Hz)
* @window_duration: duration of the sampling window in 0.1ms
* @cnt_window: number of samples in the window
* @window_stream_size: number of encoded bytes upon the sampling window
* @last_bitrate: bitrate upon the last sampling window
* @min_bitrate: minimum bitrate in kbps
* @max_bitrate: maximum bitrate in kbps
* @avg_bitrate: average bitrate in kbps
*/
struct hva_ctx_dbg {
struct dentry *debugfs_entry;
bool is_valid_period;
ktime_t begin;
u32 total_duration;
u32 cnt_duration;
u32 min_duration;
u32 max_duration;
u32 avg_duration;
u32 max_fps;
u32 total_period;
u32 cnt_period;
u32 min_period;
u32 max_period;
u32 avg_period;
u32 total_stream_size;
u32 avg_fps;
u32 window_duration;
u32 cnt_window;
u32 window_stream_size;
u32 last_bitrate;
u32 min_bitrate;
u32 max_bitrate;
u32 avg_bitrate;
};
#endif
struct hva_dev;
struct hva_enc;
/**
* struct hva_ctx - context of hva instance
*
* @hva_dev: the device that this instance is associated with
* @fh: V4L2 file handle
* @ctrl_handler: V4L2 controls handler
* @ctrls: hva controls set
* @id: instance identifier
* @aborting: true if current job aborted
* @name: instance name (debug purpose)
* @run_work: encode work
* @lock: mutex used to lock access of this context
* @flags: validity of streaminfo and frameinfo fields
* @frame_num: frame number
* @stream_num: stream number
* @max_stream_size: maximum size in bytes required for stream data
* @colorspace: colorspace identifier
* @xfer_func: transfer function identifier
* @ycbcr_enc: Y'CbCr encoding identifier
* @quantization: quantization identifier
* @streaminfo: stream properties
* @frameinfo: frame properties
* @enc: current encoder
* @priv: private codec data for this instance, allocated
* by encoder @open time
* @hw_err: true if hardware error detected
* @encoded_frames: number of encoded frames
* @sys_errors: number of system errors (memory, resource, pm...)
* @encode_errors: number of encoding errors (hw/driver errors)
* @frame_errors: number of frame errors (format, size, header...)
* @dbg: context debug info
*/
struct hva_ctx {
struct hva_dev *hva_dev;
struct v4l2_fh fh;
struct v4l2_ctrl_handler ctrl_handler;
struct hva_controls ctrls;
u8 id;
bool aborting;
char name[100];
struct work_struct run_work;
/* mutex protecting this data structure */
struct mutex lock;
u32 flags;
u32 frame_num;
u32 stream_num;
u32 max_stream_size;
enum v4l2_colorspace colorspace;
enum v4l2_xfer_func xfer_func;
enum v4l2_ycbcr_encoding ycbcr_enc;
enum v4l2_quantization quantization;
struct hva_streaminfo streaminfo;
struct hva_frameinfo frameinfo;
struct hva_enc *enc;
void *priv;
bool hw_err;
u32 encoded_frames;
u32 sys_errors;
u32 encode_errors;
u32 frame_errors;
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
struct hva_ctx_dbg dbg;
#endif
};
#define HVA_FLAG_STREAMINFO 0x0001
#define HVA_FLAG_FRAMEINFO 0x0002
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
/**
* struct hva_dev_dbg - device debug info
*
* @debugfs_entry: debugfs entry
* @last_ctx: debug information about last running instance context
*/
struct hva_dev_dbg {
struct dentry *debugfs_entry;
struct hva_ctx last_ctx;
};
#endif
#define HVA_MAX_INSTANCES 16
#define HVA_MAX_ENCODERS 10
#define HVA_MAX_FORMATS HVA_MAX_ENCODERS
/**
* struct hva_dev - abstraction for hva entity
*
* @v4l2_dev: V4L2 device
* @vdev: video device
* @pdev: platform device
* @dev: device
* @lock: mutex used for critical sections & V4L2 ops
* serialization
* @m2m_dev: memory-to-memory V4L2 device information
* @instances: opened instances
* @nb_of_instances: number of opened instances
* @instance_id: rolling counter identifying an instance (debug purpose)
* @regs: register io memory access
* @esram_addr: esram address
* @esram_size: esram size
* @clk: hva clock
* @irq_its: status interruption
* @irq_err: error interruption
* @work_queue: work queue to handle the encode jobs
* @protect_mutex: mutex used to lock access of hardware
* @interrupt: completion interrupt
* @ip_version: IP hardware version
* @encoders: registered encoders
* @nb_of_encoders: number of registered encoders
* @pixelformats: supported uncompressed video formats
* @nb_of_pixelformats: number of supported umcompressed video formats
* @streamformats: supported compressed video formats
* @nb_of_streamformats: number of supported compressed video formats
* @sfl_reg: status fifo level register value
* @sts_reg: status register value
* @lmi_err_reg: local memory interface error register value
* @emi_err_reg: external memory interface error register value
* @hec_mif_err_reg: HEC memory interface error register value
* @dbg: device debug info
*/
struct hva_dev {
struct v4l2_device v4l2_dev;
struct video_device *vdev;
struct platform_device *pdev;
struct device *dev;
/* mutex protecting vb2_queue structure */
struct mutex lock;
struct v4l2_m2m_dev *m2m_dev;
struct hva_ctx *instances[HVA_MAX_INSTANCES];
unsigned int nb_of_instances;
unsigned int instance_id;
void __iomem *regs;
u32 esram_addr;
u32 esram_size;
struct clk *clk;
int irq_its;
int irq_err;
struct workqueue_struct *work_queue;
/* mutex protecting hardware access */
struct mutex protect_mutex;
struct completion interrupt;
unsigned long int ip_version;
const struct hva_enc *encoders[HVA_MAX_ENCODERS];
u32 nb_of_encoders;
u32 pixelformats[HVA_MAX_FORMATS];
u32 nb_of_pixelformats;
u32 streamformats[HVA_MAX_FORMATS];
u32 nb_of_streamformats;
u32 sfl_reg;
u32 sts_reg;
u32 lmi_err_reg;
u32 emi_err_reg;
u32 hec_mif_err_reg;
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
struct hva_dev_dbg dbg;
#endif
};
/**
* struct hva_enc - hva encoder
*
* @name: encoder name
* @streamformat: fourcc code for compressed video format (H.264...)
* @pixelformat: fourcc code for uncompressed video format
* @max_width: maximum width of frame for this encoder
* @max_height: maximum height of frame for this encoder
* @open: open encoder
* @close: close encoder
* @encode: encode a frame (struct hva_frame) in a stream
* (struct hva_stream)
*/
struct hva_enc {
const char *name;
u32 streamformat;
u32 pixelformat;
u32 max_width;
u32 max_height;
int (*open)(struct hva_ctx *ctx);
int (*close)(struct hva_ctx *ctx);
int (*encode)(struct hva_ctx *ctx, struct hva_frame *frame,
struct hva_stream *stream);
};
#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
void hva_debugfs_create(struct hva_dev *hva);
void hva_debugfs_remove(struct hva_dev *hva);
void hva_dbg_ctx_create(struct hva_ctx *ctx);
void hva_dbg_ctx_remove(struct hva_ctx *ctx);
void hva_dbg_perf_begin(struct hva_ctx *ctx);
void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream);
#endif
#endif /* HVA_H */