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,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 */