On Wed, 01 Feb 2017, Hugues Fruchet wrote:

> IPC (Inter Process Communication) support for communication with
> DELTA coprocessor firmware using rpmsg kernel framework.
> Based on 4 services open/set_stream/decode/close and their associated
> rpmsg messages.
> The messages structures are duplicated on both host and firmware
> side and are packed (use only of 32 bits size fields in messages
> structures to ensure packing).
> Each service is synchronous; service returns only when firmware
> acknowledges the associated command message.
> Due to significant parameters size exchanged from host to copro,
> parameters are not inserted in rpmsg messages. Instead, parameters are
> stored in physical memory shared between host and coprocessor.
> Memory is non-cacheable, so no special operation is required
> to ensure memory coherency on host and on coprocessor side.
> Multi-instance support and re-entrance are ensured using host_hdl and
> copro_hdl in message header exchanged between both host and coprocessor.
> This avoids to manage tables on both sides to get back the running context
> of each instance.
> 
> Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>

Acked-by: Peter Griffin <peter.grif...@linaro.org>

> ---
>  drivers/media/platform/Kconfig                |   1 +
>  drivers/media/platform/sti/delta/Makefile     |   2 +-
>  drivers/media/platform/sti/delta/delta-ipc.c  | 594 
> ++++++++++++++++++++++++++
>  drivers/media/platform/sti/delta/delta-ipc.h  |  76 ++++
>  drivers/media/platform/sti/delta/delta-v4l2.c |  11 +
>  drivers/media/platform/sti/delta/delta.h      |  21 +
>  6 files changed, 704 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/platform/sti/delta/delta-ipc.c
>  create mode 100644 drivers/media/platform/sti/delta/delta-ipc.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 2247d9d..2e82ec6 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -323,6 +323,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 93a3037..b791ba0 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 0000000..41e4a4c
> --- /dev/null
> +++ b/drivers/media/platform/sti/delta/delta-ipc.c
> @@ -0,0 +1,594 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Author: Hugues Fruchet <hugues.fruc...@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#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;
> +
> +     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 0000000..cef2019
> --- /dev/null
> +++ b/drivers/media/platform/sti/delta/delta-ipc.h
> @@ -0,0 +1,76 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Author: Hugues Fruchet <hugues.fruc...@st.com> 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 e65a3a3..237a938 100644
> --- a/drivers/media/platform/sti/delta/delta-v4l2.c
> +++ b/drivers/media/platform/sti/delta/delta-v4l2.c
> @@ -17,6 +17,7 @@
>  #include <media/videobuf2-dma-contig.h>
>  
>  #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 9e26525..d4a401b 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 <linux/rpmsg.h>
>  #include <media/v4l2-device.h>
>  #include <media/v4l2-mem2mem.h>
>  
> @@ -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_dec {
>   * @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)
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to