linux/drivers/media/platform/sti/delta/delta-ipc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) STMicroelectronics SA 2015
   4 * Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
   5 */
   6
   7#include <linux/rpmsg.h>
   8
   9#include "delta.h"
  10#include "delta-ipc.h"
  11#include "delta-mem.h"
  12
  13#define IPC_TIMEOUT 100
  14#define IPC_SANITY_TAG 0xDEADBEEF
  15
  16enum delta_ipc_fw_command {
  17        DELTA_IPC_OPEN,
  18        DELTA_IPC_SET_STREAM,
  19        DELTA_IPC_DECODE,
  20        DELTA_IPC_CLOSE
  21};
  22
  23#define to_rpmsg_driver(__drv) container_of(__drv, struct rpmsg_driver, drv)
  24#define to_delta(__d) container_of(__d, struct delta_dev, rpmsg_driver)
  25
  26#define to_ctx(hdl) ((struct delta_ipc_ctx *)hdl)
  27#define to_pctx(ctx) container_of(ctx, struct delta_ctx, ipc_ctx)
  28
  29struct delta_ipc_header_msg {
  30        u32 tag;
  31        void *host_hdl;
  32        u32 copro_hdl;
  33        u32 command;
  34};
  35
  36#define to_host_hdl(ctx) ((void *)ctx)
  37
  38#define msg_to_ctx(msg) ((struct delta_ipc_ctx *)(msg)->header.host_hdl)
  39#define msg_to_copro_hdl(msg) ((msg)->header.copro_hdl)
  40
  41static inline dma_addr_t to_paddr(struct delta_ipc_ctx *ctx, void *vaddr)
  42{
  43        return (ctx->ipc_buf->paddr + (vaddr - ctx->ipc_buf->vaddr));
  44}
  45
  46static inline bool is_valid_data(struct delta_ipc_ctx *ctx,
  47                                 void *data, u32 size)
  48{
  49        return ((data >= ctx->ipc_buf->vaddr) &&
  50                ((data + size) <= (ctx->ipc_buf->vaddr + ctx->ipc_buf->size)));
  51}
  52
  53/*
  54 * IPC shared memory (@ipc_buf_size, @ipc_buf_paddr) is sent to copro
  55 * at each instance opening. This memory is allocated by IPC client
  56 * and given through delta_ipc_open(). All messages parameters
  57 * (open, set_stream, decode) will have their phy address within
  58 * this IPC shared memory, avoiding de-facto recopies inside delta-ipc.
  59 * All the below messages structures are used on both host and firmware
  60 * side and are packed (use only of 32 bits size fields in messages
  61 * structures to ensure packing):
  62 * - struct delta_ipc_open_msg
  63 * - struct delta_ipc_set_stream_msg
  64 * - struct delta_ipc_decode_msg
  65 * - struct delta_ipc_close_msg
  66 * - struct delta_ipc_cb_msg
  67 */
  68struct delta_ipc_open_msg {
  69        struct delta_ipc_header_msg header;
  70        u32 ipc_buf_size;
  71        dma_addr_t ipc_buf_paddr;
  72        char name[32];
  73        u32 param_size;
  74        dma_addr_t param_paddr;
  75};
  76
  77struct delta_ipc_set_stream_msg {
  78        struct delta_ipc_header_msg header;
  79        u32 param_size;
  80        dma_addr_t param_paddr;
  81};
  82
  83struct delta_ipc_decode_msg {
  84        struct delta_ipc_header_msg header;
  85        u32 param_size;
  86        dma_addr_t param_paddr;
  87        u32 status_size;
  88        dma_addr_t status_paddr;
  89};
  90
  91struct delta_ipc_close_msg {
  92        struct delta_ipc_header_msg header;
  93};
  94
  95struct delta_ipc_cb_msg {
  96        struct delta_ipc_header_msg header;
  97        int err;
  98};
  99
 100static void build_msg_header(struct delta_ipc_ctx *ctx,
 101                             enum delta_ipc_fw_command command,
 102                             struct delta_ipc_header_msg *header)
 103{
 104        header->tag = IPC_SANITY_TAG;
 105        header->host_hdl = to_host_hdl(ctx);
 106        header->copro_hdl = ctx->copro_hdl;
 107        header->command = command;
 108}
 109
 110int delta_ipc_open(struct delta_ctx *pctx, const char *name,
 111                   struct delta_ipc_param *param, u32 ipc_buf_size,
 112                   struct delta_buf **ipc_buf, void **hdl)
 113{
 114        struct delta_dev *delta = pctx->dev;
 115        struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
 116        struct delta_ipc_ctx *ctx = &pctx->ipc_ctx;
 117        struct delta_ipc_open_msg msg;
 118        struct delta_buf *buf = &ctx->ipc_buf_struct;
 119        int ret;
 120
 121        if (!rpmsg_device) {
 122                dev_err(delta->dev,
 123                        "%s   ipc: failed to open, rpmsg is not initialized\n",
 124                        pctx->name);
 125                pctx->sys_errors++;
 126                return -EINVAL;
 127        }
 128
 129        if (!name) {
 130                dev_err(delta->dev,
 131                        "%s   ipc: failed to open, no name given\n",
 132                        pctx->name);
 133                return -EINVAL;
 134        }
 135
 136        if (!param || !param->data || !param->size) {
 137                dev_err(delta->dev,
 138                        "%s  ipc: failed to open, empty parameter\n",
 139                        pctx->name);
 140                return -EINVAL;
 141        }
 142
 143        if (!ipc_buf_size) {
 144                dev_err(delta->dev,
 145                        "%s   ipc: failed to open, no size given for ipc buffer\n",
 146                        pctx->name);
 147                return -EINVAL;
 148        }
 149
 150        if (param->size > ipc_buf_size) {
 151                dev_err(delta->dev,
 152                        "%s   ipc: failed to open, too large ipc parameter (%d bytes while max %d expected)\n",
 153                        pctx->name,
 154                        param->size, ctx->ipc_buf->size);
 155                return -EINVAL;
 156        }
 157
 158        /* init */
 159        init_completion(&ctx->done);
 160
 161        /*
 162         * allocation of contiguous buffer for
 163         * data of commands exchanged between
 164         * host and firmware coprocessor
 165         */
 166        ret = hw_alloc(pctx, ipc_buf_size,
 167                       "ipc data buffer", buf);
 168        if (ret)
 169                return ret;
 170        ctx->ipc_buf = buf;
 171
 172        /* build rpmsg message */
 173        build_msg_header(ctx, DELTA_IPC_OPEN, &msg.header);
 174
 175        msg.ipc_buf_size = ipc_buf_size;
 176        msg.ipc_buf_paddr = ctx->ipc_buf->paddr;
 177
 178        strscpy(msg.name, name, sizeof(msg.name));
 179
 180        msg.param_size = param->size;
 181        memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size);
 182        msg.param_paddr = ctx->ipc_buf->paddr;
 183
 184        /* send it */
 185        ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
 186        if (ret) {
 187                dev_err(delta->dev,
 188                        "%s   ipc: failed to open, rpmsg_send failed (%d) for DELTA_IPC_OPEN (name=%s, size=%d, data=%p)\n",
 189                        pctx->name,
 190                        ret, name, param->size, param->data);
 191                goto err;
 192        }
 193
 194        /* wait for acknowledge */
 195        if (!wait_for_completion_timeout
 196            (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
 197                dev_err(delta->dev,
 198                        "%s   ipc: failed to open, timeout waiting for DELTA_IPC_OPEN callback (name=%s, size=%d, data=%p)\n",
 199                        pctx->name,
 200                        name, param->size, param->data);
 201                ret = -ETIMEDOUT;
 202                goto err;
 203        }
 204
 205        /* command completed, check error */
 206        if (ctx->cb_err) {
 207                dev_err(delta->dev,
 208                        "%s   ipc: failed to open, DELTA_IPC_OPEN completed but with error (%d) (name=%s, size=%d, data=%p)\n",
 209                        pctx->name,
 210                        ctx->cb_err, name, param->size, param->data);
 211                ret = -EIO;
 212                goto err;
 213        }
 214
 215        *ipc_buf = ctx->ipc_buf;
 216        *hdl = (void *)ctx;
 217
 218        return 0;
 219
 220err:
 221        pctx->sys_errors++;
 222        hw_free(pctx, ctx->ipc_buf);
 223        ctx->ipc_buf = NULL;
 224
 225        return ret;
 226};
 227
 228int delta_ipc_set_stream(void *hdl, struct delta_ipc_param *param)
 229{
 230        struct delta_ipc_ctx *ctx = to_ctx(hdl);
 231        struct delta_ctx *pctx = to_pctx(ctx);
 232        struct delta_dev *delta = pctx->dev;
 233        struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
 234        struct delta_ipc_set_stream_msg msg;
 235        int ret;
 236
 237        if (!hdl) {
 238                dev_err(delta->dev,
 239                        "%s   ipc: failed to set stream, invalid ipc handle\n",
 240                        pctx->name);
 241                return -EINVAL;
 242        }
 243
 244        if (!rpmsg_device) {
 245                dev_err(delta->dev,
 246                        "%s   ipc: failed to set stream, rpmsg is not initialized\n",
 247                        pctx->name);
 248                return -EINVAL;
 249        }
 250
 251        if (!param || !param->data || !param->size) {
 252                dev_err(delta->dev,
 253                        "%s  ipc: failed to set stream, empty parameter\n",
 254                        pctx->name);
 255                return -EINVAL;
 256        }
 257
 258        if (param->size > ctx->ipc_buf->size) {
 259                dev_err(delta->dev,
 260                        "%s   ipc: failed to set stream, too large ipc parameter(%d bytes while max %d expected)\n",
 261                        pctx->name,
 262                        param->size, ctx->ipc_buf->size);
 263                return -EINVAL;
 264        }
 265
 266        if (!is_valid_data(ctx, param->data, param->size)) {
 267                dev_err(delta->dev,
 268                        "%s   ipc: failed to set stream, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
 269                        pctx->name,
 270                        param->size,
 271                        param->data,
 272                        ctx->ipc_buf->vaddr,
 273                        ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
 274                return -EINVAL;
 275        }
 276
 277        /* build rpmsg message */
 278        build_msg_header(ctx, DELTA_IPC_SET_STREAM, &msg.header);
 279
 280        msg.param_size = param->size;
 281        msg.param_paddr = to_paddr(ctx, param->data);
 282
 283        /* send it */
 284        ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
 285        if (ret) {
 286                dev_err(delta->dev,
 287                        "%s   ipc: failed to set stream, rpmsg_send failed (%d) for DELTA_IPC_SET_STREAM (size=%d, data=%p)\n",
 288                        pctx->name,
 289                        ret, param->size, param->data);
 290                pctx->sys_errors++;
 291                return ret;
 292        }
 293
 294        /* wait for acknowledge */
 295        if (!wait_for_completion_timeout
 296            (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
 297                dev_err(delta->dev,
 298                        "%s   ipc: failed to set stream, timeout waiting for DELTA_IPC_SET_STREAM callback (size=%d, data=%p)\n",
 299                        pctx->name,
 300                        param->size, param->data);
 301                pctx->sys_errors++;
 302                return -ETIMEDOUT;
 303        }
 304
 305        /* command completed, check status */
 306        if (ctx->cb_err) {
 307                dev_err(delta->dev,
 308                        "%s   ipc: failed to set stream, DELTA_IPC_SET_STREAM completed but with error (%d) (size=%d, data=%p)\n",
 309                        pctx->name,
 310                        ctx->cb_err, param->size, param->data);
 311                pctx->sys_errors++;
 312                return -EIO;
 313        }
 314
 315        return 0;
 316}
 317
 318int delta_ipc_decode(void *hdl, struct delta_ipc_param *param,
 319                     struct delta_ipc_param *status)
 320{
 321        struct delta_ipc_ctx *ctx = to_ctx(hdl);
 322        struct delta_ctx *pctx = to_pctx(ctx);
 323        struct delta_dev *delta = pctx->dev;
 324        struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
 325        struct delta_ipc_decode_msg msg;
 326        int ret;
 327
 328        if (!hdl) {
 329                dev_err(delta->dev,
 330                        "%s   ipc: failed to decode, invalid ipc handle\n",
 331                        pctx->name);
 332                return -EINVAL;
 333        }
 334
 335        if (!rpmsg_device) {
 336                dev_err(delta->dev,
 337                        "%s   ipc: failed to decode, rpmsg is not initialized\n",
 338                        pctx->name);
 339                return -EINVAL;
 340        }
 341
 342        if (!param || !param->data || !param->size) {
 343                dev_err(delta->dev,
 344                        "%s  ipc: failed to decode, empty parameter\n",
 345                        pctx->name);
 346                return -EINVAL;
 347        }
 348
 349        if (!status || !status->data || !status->size) {
 350                dev_err(delta->dev,
 351                        "%s  ipc: failed to decode, empty status\n",
 352                        pctx->name);
 353                return -EINVAL;
 354        }
 355
 356        if (param->size + status->size > ctx->ipc_buf->size) {
 357                dev_err(delta->dev,
 358                        "%s   ipc: failed to decode, too large ipc parameter (%d bytes (param) + %d bytes (status) while max %d expected)\n",
 359                        pctx->name,
 360                        param->size,
 361                        status->size,
 362                        ctx->ipc_buf->size);
 363                return -EINVAL;
 364        }
 365
 366        if (!is_valid_data(ctx, param->data, param->size)) {
 367                dev_err(delta->dev,
 368                        "%s   ipc: failed to decode, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
 369                        pctx->name,
 370                        param->size,
 371                        param->data,
 372                        ctx->ipc_buf->vaddr,
 373                        ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
 374                return -EINVAL;
 375        }
 376
 377        if (!is_valid_data(ctx, status->data, status->size)) {
 378                dev_err(delta->dev,
 379                        "%s   ipc: failed to decode, status is not in expected address range (size=%d, data=%p not in %p..%p)\n",
 380                        pctx->name,
 381                        status->size,
 382                        status->data,
 383                        ctx->ipc_buf->vaddr,
 384                        ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
 385                return -EINVAL;
 386        }
 387
 388        /* build rpmsg message */
 389        build_msg_header(ctx, DELTA_IPC_DECODE, &msg.header);
 390
 391        msg.param_size = param->size;
 392        msg.param_paddr = to_paddr(ctx, param->data);
 393
 394        msg.status_size = status->size;
 395        msg.status_paddr = to_paddr(ctx, status->data);
 396
 397        /* send it */
 398        ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
 399        if (ret) {
 400                dev_err(delta->dev,
 401                        "%s   ipc: failed to decode, rpmsg_send failed (%d) for DELTA_IPC_DECODE (size=%d, data=%p)\n",
 402                        pctx->name,
 403                        ret, param->size, param->data);
 404                pctx->sys_errors++;
 405                return ret;
 406        }
 407
 408        /* wait for acknowledge */
 409        if (!wait_for_completion_timeout
 410            (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
 411                dev_err(delta->dev,
 412                        "%s   ipc: failed to decode, timeout waiting for DELTA_IPC_DECODE callback (size=%d, data=%p)\n",
 413                        pctx->name,
 414                        param->size, param->data);
 415                pctx->sys_errors++;
 416                return -ETIMEDOUT;
 417        }
 418
 419        /* command completed, check status */
 420        if (ctx->cb_err) {
 421                dev_err(delta->dev,
 422                        "%s   ipc: failed to decode, DELTA_IPC_DECODE completed but with error (%d) (size=%d, data=%p)\n",
 423                        pctx->name,
 424                        ctx->cb_err, param->size, param->data);
 425                pctx->sys_errors++;
 426                return -EIO;
 427        }
 428
 429        return 0;
 430};
 431
 432void delta_ipc_close(void *hdl)
 433{
 434        struct delta_ipc_ctx *ctx = to_ctx(hdl);
 435        struct delta_ctx *pctx = to_pctx(ctx);
 436        struct delta_dev *delta = pctx->dev;
 437        struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
 438        struct delta_ipc_close_msg msg;
 439        int ret;
 440
 441        if (!hdl) {
 442                dev_err(delta->dev,
 443                        "%s   ipc: failed to close, invalid ipc handle\n",
 444                        pctx->name);
 445                return;
 446        }
 447
 448        if (ctx->ipc_buf) {
 449                hw_free(pctx, ctx->ipc_buf);
 450                ctx->ipc_buf = NULL;
 451        }
 452
 453        if (!rpmsg_device) {
 454                dev_err(delta->dev,
 455                        "%s   ipc: failed to close, rpmsg is not initialized\n",
 456                        pctx->name);
 457                return;
 458        }
 459
 460        /* build rpmsg message */
 461        build_msg_header(ctx, DELTA_IPC_CLOSE, &msg.header);
 462
 463        /* send it */
 464        ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
 465        if (ret) {
 466                dev_err(delta->dev,
 467                        "%s   ipc: failed to close, rpmsg_send failed (%d) for DELTA_IPC_CLOSE\n",
 468                        pctx->name, ret);
 469                pctx->sys_errors++;
 470                return;
 471        }
 472
 473        /* wait for acknowledge */
 474        if (!wait_for_completion_timeout
 475            (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
 476                dev_err(delta->dev,
 477                        "%s   ipc: failed to close, timeout waiting for DELTA_IPC_CLOSE callback\n",
 478                        pctx->name);
 479                pctx->sys_errors++;
 480                return;
 481        }
 482
 483        /* command completed, check status */
 484        if (ctx->cb_err) {
 485                dev_err(delta->dev,
 486                        "%s   ipc: failed to close, DELTA_IPC_CLOSE completed but with error (%d)\n",
 487                        pctx->name, ctx->cb_err);
 488                pctx->sys_errors++;
 489        }
 490};
 491
 492static int delta_ipc_cb(struct rpmsg_device *rpdev, void *data,
 493                        int len, void *priv, u32 src)
 494{
 495        struct delta_ipc_ctx *ctx;
 496        struct delta_ipc_cb_msg *msg;
 497
 498        /* sanity check */
 499        if (!rpdev) {
 500                dev_err(NULL, "rpdev is NULL\n");
 501                return -EINVAL;
 502        }
 503
 504        if (!data || !len) {
 505                dev_err(&rpdev->dev,
 506                        "unexpected empty message received from src=%d\n", src);
 507                return -EINVAL;
 508        }
 509
 510        if (len != sizeof(*msg)) {
 511                dev_err(&rpdev->dev,
 512                        "unexpected message length received from src=%d (received %d bytes while %zu bytes expected)\n",
 513                        len, src, sizeof(*msg));
 514                return -EINVAL;
 515        }
 516
 517        msg = (struct delta_ipc_cb_msg *)data;
 518        if (msg->header.tag != IPC_SANITY_TAG) {
 519                dev_err(&rpdev->dev,
 520                        "unexpected message tag received from src=%d (received %x tag while %x expected)\n",
 521                        src, msg->header.tag, IPC_SANITY_TAG);
 522                return -EINVAL;
 523        }
 524
 525        ctx = msg_to_ctx(msg);
 526        if (!ctx) {
 527                dev_err(&rpdev->dev,
 528                        "unexpected message with NULL host_hdl received from src=%d\n",
 529                        src);
 530                return -EINVAL;
 531        }
 532
 533        /*
 534         * if not already known, save copro instance context
 535         * to ensure re-entrance on copro side
 536         */
 537        if (!ctx->copro_hdl)
 538                ctx->copro_hdl = msg_to_copro_hdl(msg);
 539
 540        /*
 541         * all is fine,
 542         * update status & complete command
 543         */
 544        ctx->cb_err = msg->err;
 545        complete(&ctx->done);
 546
 547        return 0;
 548}
 549
 550static int delta_ipc_probe(struct rpmsg_device *rpmsg_device)
 551{
 552        struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);
 553        struct delta_dev *delta = to_delta(rpdrv);
 554
 555        delta->rpmsg_device = rpmsg_device;
 556
 557        return 0;
 558}
 559
 560static void delta_ipc_remove(struct rpmsg_device *rpmsg_device)
 561{
 562        struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);
 563        struct delta_dev *delta = to_delta(rpdrv);
 564
 565        delta->rpmsg_device = NULL;
 566}
 567
 568static struct rpmsg_device_id delta_ipc_device_id_table[] = {
 569        {.name = "rpmsg-delta"},
 570        {},
 571};
 572
 573static struct rpmsg_driver delta_rpmsg_driver = {
 574        .drv = {.name = KBUILD_MODNAME},
 575        .id_table = delta_ipc_device_id_table,
 576        .probe = delta_ipc_probe,
 577        .callback = delta_ipc_cb,
 578        .remove = delta_ipc_remove,
 579};
 580
 581int delta_ipc_init(struct delta_dev *delta)
 582{
 583        delta->rpmsg_driver = delta_rpmsg_driver;
 584
 585        return register_rpmsg_driver(&delta->rpmsg_driver);
 586}
 587
 588void delta_ipc_exit(struct delta_dev *delta)
 589{
 590        unregister_rpmsg_driver(&delta->rpmsg_driver);
 591}
 592