diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 863c61a0895e..f84f8c705562 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -337,6 +337,7 @@ config VIDEO_STI_DELTA_DRIVER default n select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV + select RPMSG endif # VIDEO_STI_DELTA diff --git a/drivers/media/platform/sti/delta/Makefile b/drivers/media/platform/sti/delta/Makefile index 93a3037c29b9..b791ba09fafd 100644 --- a/drivers/media/platform/sti/delta/Makefile +++ b/drivers/media/platform/sti/delta/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) := st-delta.o -st-delta-y := delta-v4l2.o delta-mem.o +st-delta-y := delta-v4l2.o delta-mem.o delta-ipc.o diff --git a/drivers/media/platform/sti/delta/delta-ipc.c b/drivers/media/platform/sti/delta/delta-ipc.c new file mode 100644 index 000000000000..41e4a4c259b3 --- /dev/null +++ b/drivers/media/platform/sti/delta/delta-ipc.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Author: Hugues Fruchet for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include + +#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; + + memcpy(msg.name, name, sizeof(msg.name)); + msg.name[sizeof(msg.name) - 1] = 0; + + 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++; + if (ctx->ipc_buf) { + 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); +} diff --git a/drivers/media/platform/sti/delta/delta-ipc.h b/drivers/media/platform/sti/delta/delta-ipc.h new file mode 100644 index 000000000000..cef2019c72d4 --- /dev/null +++ b/drivers/media/platform/sti/delta/delta-ipc.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Author: Hugues Fruchet for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#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 */ diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index e65a3a327724..237a938a9e6b 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -17,6 +17,7 @@ #include #include "delta.h" +#include "delta-ipc.h" #define DELTA_NAME "st-delta" @@ -1703,6 +1704,14 @@ static int delta_probe(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + /* init firmware ipc channel */ + ret = delta_ipc_init(delta); + if (ret) { + dev_err(delta->dev, "%s failed to initialize firmware ipc channel\n", + DELTA_PREFIX); + goto err; + } + /* register all available decoders */ register_decoders(delta); @@ -1747,6 +1756,8 @@ static int delta_remove(struct platform_device *pdev) { struct delta_dev *delta = platform_get_drvdata(pdev); + delta_ipc_exit(delta); + delta_unregister_device(delta); destroy_workqueue(delta->work_queue); diff --git a/drivers/media/platform/sti/delta/delta.h b/drivers/media/platform/sti/delta/delta.h index 9e265255e30a..d4a401b87f8b 100644 --- a/drivers/media/platform/sti/delta/delta.h +++ b/drivers/media/platform/sti/delta/delta.h @@ -7,6 +7,7 @@ #ifndef DELTA_H #define DELTA_H +#include #include #include @@ -199,6 +200,19 @@ struct delta_buf { 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; /* @@ -368,6 +382,7 @@ struct delta_dev; * @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 @@ -399,6 +414,8 @@ struct delta_ctx { 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; @@ -447,6 +464,8 @@ struct delta_ctx { * @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; @@ -466,6 +485,8 @@ struct delta_dev { 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)