linux/drivers/media/platform/qcom/venus/hfi.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
   3 * Copyright (C) 2017 Linaro Ltd.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 and
   7 * only version 2 as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 */
  15#include <linux/slab.h>
  16#include <linux/mutex.h>
  17#include <linux/list.h>
  18#include <linux/completion.h>
  19#include <linux/platform_device.h>
  20#include <linux/videodev2.h>
  21
  22#include "core.h"
  23#include "hfi.h"
  24#include "hfi_cmds.h"
  25#include "hfi_venus.h"
  26
  27#define TIMEOUT         msecs_to_jiffies(1000)
  28
  29static u32 to_codec_type(u32 pixfmt)
  30{
  31        switch (pixfmt) {
  32        case V4L2_PIX_FMT_H264:
  33        case V4L2_PIX_FMT_H264_NO_SC:
  34                return HFI_VIDEO_CODEC_H264;
  35        case V4L2_PIX_FMT_H263:
  36                return HFI_VIDEO_CODEC_H263;
  37        case V4L2_PIX_FMT_MPEG1:
  38                return HFI_VIDEO_CODEC_MPEG1;
  39        case V4L2_PIX_FMT_MPEG2:
  40                return HFI_VIDEO_CODEC_MPEG2;
  41        case V4L2_PIX_FMT_MPEG4:
  42                return HFI_VIDEO_CODEC_MPEG4;
  43        case V4L2_PIX_FMT_VC1_ANNEX_G:
  44        case V4L2_PIX_FMT_VC1_ANNEX_L:
  45                return HFI_VIDEO_CODEC_VC1;
  46        case V4L2_PIX_FMT_VP8:
  47                return HFI_VIDEO_CODEC_VP8;
  48        case V4L2_PIX_FMT_VP9:
  49                return HFI_VIDEO_CODEC_VP9;
  50        case V4L2_PIX_FMT_XVID:
  51                return HFI_VIDEO_CODEC_DIVX;
  52        default:
  53                return 0;
  54        }
  55}
  56
  57int hfi_core_init(struct venus_core *core)
  58{
  59        int ret = 0;
  60
  61        mutex_lock(&core->lock);
  62
  63        if (core->state >= CORE_INIT)
  64                goto unlock;
  65
  66        reinit_completion(&core->done);
  67
  68        ret = core->ops->core_init(core);
  69        if (ret)
  70                goto unlock;
  71
  72        ret = wait_for_completion_timeout(&core->done, TIMEOUT);
  73        if (!ret) {
  74                ret = -ETIMEDOUT;
  75                goto unlock;
  76        }
  77
  78        ret = 0;
  79
  80        if (core->error != HFI_ERR_NONE) {
  81                ret = -EIO;
  82                goto unlock;
  83        }
  84
  85        core->state = CORE_INIT;
  86unlock:
  87        mutex_unlock(&core->lock);
  88        return ret;
  89}
  90
  91int hfi_core_deinit(struct venus_core *core, bool blocking)
  92{
  93        int ret = 0, empty;
  94
  95        mutex_lock(&core->lock);
  96
  97        if (core->state == CORE_UNINIT)
  98                goto unlock;
  99
 100        empty = list_empty(&core->instances);
 101
 102        if (!empty && !blocking) {
 103                ret = -EBUSY;
 104                goto unlock;
 105        }
 106
 107        if (!empty) {
 108                mutex_unlock(&core->lock);
 109                wait_on_atomic_t(&core->insts_count, atomic_t_wait,
 110                                 TASK_UNINTERRUPTIBLE);
 111                mutex_lock(&core->lock);
 112        }
 113
 114        ret = core->ops->core_deinit(core);
 115
 116        if (!ret)
 117                core->state = CORE_UNINIT;
 118
 119unlock:
 120        mutex_unlock(&core->lock);
 121        return ret;
 122}
 123
 124int hfi_core_suspend(struct venus_core *core)
 125{
 126        if (core->state != CORE_INIT)
 127                return 0;
 128
 129        return core->ops->suspend(core);
 130}
 131
 132int hfi_core_resume(struct venus_core *core, bool force)
 133{
 134        if (!force && core->state != CORE_INIT)
 135                return 0;
 136
 137        return core->ops->resume(core);
 138}
 139
 140int hfi_core_trigger_ssr(struct venus_core *core, u32 type)
 141{
 142        return core->ops->core_trigger_ssr(core, type);
 143}
 144
 145int hfi_core_ping(struct venus_core *core)
 146{
 147        int ret;
 148
 149        mutex_lock(&core->lock);
 150
 151        ret = core->ops->core_ping(core, 0xbeef);
 152        if (ret)
 153                goto unlock;
 154
 155        ret = wait_for_completion_timeout(&core->done, TIMEOUT);
 156        if (!ret) {
 157                ret = -ETIMEDOUT;
 158                goto unlock;
 159        }
 160        ret = 0;
 161        if (core->error != HFI_ERR_NONE)
 162                ret = -ENODEV;
 163unlock:
 164        mutex_unlock(&core->lock);
 165        return ret;
 166}
 167
 168static int wait_session_msg(struct venus_inst *inst)
 169{
 170        int ret;
 171
 172        ret = wait_for_completion_timeout(&inst->done, TIMEOUT);
 173        if (!ret)
 174                return -ETIMEDOUT;
 175
 176        if (inst->error != HFI_ERR_NONE)
 177                return -EIO;
 178
 179        return 0;
 180}
 181
 182int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops)
 183{
 184        struct venus_core *core = inst->core;
 185
 186        if (!ops)
 187                return -EINVAL;
 188
 189        inst->state = INST_UNINIT;
 190        init_completion(&inst->done);
 191        inst->ops = ops;
 192
 193        mutex_lock(&core->lock);
 194        list_add_tail(&inst->list, &core->instances);
 195        atomic_inc(&core->insts_count);
 196        mutex_unlock(&core->lock);
 197
 198        return 0;
 199}
 200EXPORT_SYMBOL_GPL(hfi_session_create);
 201
 202int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
 203{
 204        struct venus_core *core = inst->core;
 205        const struct hfi_ops *ops = core->ops;
 206        u32 codec;
 207        int ret;
 208
 209        codec = to_codec_type(pixfmt);
 210        reinit_completion(&inst->done);
 211
 212        ret = ops->session_init(inst, inst->session_type, codec);
 213        if (ret)
 214                return ret;
 215
 216        ret = wait_session_msg(inst);
 217        if (ret)
 218                return ret;
 219
 220        inst->state = INST_INIT;
 221
 222        return 0;
 223}
 224EXPORT_SYMBOL_GPL(hfi_session_init);
 225
 226void hfi_session_destroy(struct venus_inst *inst)
 227{
 228        struct venus_core *core = inst->core;
 229
 230        mutex_lock(&core->lock);
 231        list_del_init(&inst->list);
 232        atomic_dec(&core->insts_count);
 233        wake_up_atomic_t(&core->insts_count);
 234        mutex_unlock(&core->lock);
 235}
 236EXPORT_SYMBOL_GPL(hfi_session_destroy);
 237
 238int hfi_session_deinit(struct venus_inst *inst)
 239{
 240        const struct hfi_ops *ops = inst->core->ops;
 241        int ret;
 242
 243        if (inst->state == INST_UNINIT)
 244                return 0;
 245
 246        if (inst->state < INST_INIT)
 247                return -EINVAL;
 248
 249        reinit_completion(&inst->done);
 250
 251        ret = ops->session_end(inst);
 252        if (ret)
 253                return ret;
 254
 255        ret = wait_session_msg(inst);
 256        if (ret)
 257                return ret;
 258
 259        inst->state = INST_UNINIT;
 260
 261        return 0;
 262}
 263EXPORT_SYMBOL_GPL(hfi_session_deinit);
 264
 265int hfi_session_start(struct venus_inst *inst)
 266{
 267        const struct hfi_ops *ops = inst->core->ops;
 268        int ret;
 269
 270        if (inst->state != INST_LOAD_RESOURCES)
 271                return -EINVAL;
 272
 273        reinit_completion(&inst->done);
 274
 275        ret = ops->session_start(inst);
 276        if (ret)
 277                return ret;
 278
 279        ret = wait_session_msg(inst);
 280        if (ret)
 281                return ret;
 282
 283        inst->state = INST_START;
 284
 285        return 0;
 286}
 287
 288int hfi_session_stop(struct venus_inst *inst)
 289{
 290        const struct hfi_ops *ops = inst->core->ops;
 291        int ret;
 292
 293        if (inst->state != INST_START)
 294                return -EINVAL;
 295
 296        reinit_completion(&inst->done);
 297
 298        ret = ops->session_stop(inst);
 299        if (ret)
 300                return ret;
 301
 302        ret = wait_session_msg(inst);
 303        if (ret)
 304                return ret;
 305
 306        inst->state = INST_STOP;
 307
 308        return 0;
 309}
 310
 311int hfi_session_continue(struct venus_inst *inst)
 312{
 313        struct venus_core *core = inst->core;
 314
 315        if (core->res->hfi_version != HFI_VERSION_3XX)
 316                return 0;
 317
 318        return core->ops->session_continue(inst);
 319}
 320EXPORT_SYMBOL_GPL(hfi_session_continue);
 321
 322int hfi_session_abort(struct venus_inst *inst)
 323{
 324        const struct hfi_ops *ops = inst->core->ops;
 325        int ret;
 326
 327        reinit_completion(&inst->done);
 328
 329        ret = ops->session_abort(inst);
 330        if (ret)
 331                return ret;
 332
 333        ret = wait_session_msg(inst);
 334        if (ret)
 335                return ret;
 336
 337        return 0;
 338}
 339
 340int hfi_session_load_res(struct venus_inst *inst)
 341{
 342        const struct hfi_ops *ops = inst->core->ops;
 343        int ret;
 344
 345        if (inst->state != INST_INIT)
 346                return -EINVAL;
 347
 348        reinit_completion(&inst->done);
 349
 350        ret = ops->session_load_res(inst);
 351        if (ret)
 352                return ret;
 353
 354        ret = wait_session_msg(inst);
 355        if (ret)
 356                return ret;
 357
 358        inst->state = INST_LOAD_RESOURCES;
 359
 360        return 0;
 361}
 362
 363int hfi_session_unload_res(struct venus_inst *inst)
 364{
 365        const struct hfi_ops *ops = inst->core->ops;
 366        int ret;
 367
 368        if (inst->state != INST_STOP)
 369                return -EINVAL;
 370
 371        reinit_completion(&inst->done);
 372
 373        ret = ops->session_release_res(inst);
 374        if (ret)
 375                return ret;
 376
 377        ret = wait_session_msg(inst);
 378        if (ret)
 379                return ret;
 380
 381        inst->state = INST_RELEASE_RESOURCES;
 382
 383        return 0;
 384}
 385
 386int hfi_session_flush(struct venus_inst *inst)
 387{
 388        const struct hfi_ops *ops = inst->core->ops;
 389        int ret;
 390
 391        reinit_completion(&inst->done);
 392
 393        ret = ops->session_flush(inst, HFI_FLUSH_ALL);
 394        if (ret)
 395                return ret;
 396
 397        ret = wait_session_msg(inst);
 398        if (ret)
 399                return ret;
 400
 401        return 0;
 402}
 403EXPORT_SYMBOL_GPL(hfi_session_flush);
 404
 405int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd)
 406{
 407        const struct hfi_ops *ops = inst->core->ops;
 408
 409        return ops->session_set_buffers(inst, bd);
 410}
 411
 412int hfi_session_unset_buffers(struct venus_inst *inst,
 413                              struct hfi_buffer_desc *bd)
 414{
 415        const struct hfi_ops *ops = inst->core->ops;
 416        int ret;
 417
 418        reinit_completion(&inst->done);
 419
 420        ret = ops->session_unset_buffers(inst, bd);
 421        if (ret)
 422                return ret;
 423
 424        if (!bd->response_required)
 425                return 0;
 426
 427        ret = wait_session_msg(inst);
 428        if (ret)
 429                return ret;
 430
 431        return 0;
 432}
 433
 434int hfi_session_get_property(struct venus_inst *inst, u32 ptype,
 435                             union hfi_get_property *hprop)
 436{
 437        const struct hfi_ops *ops = inst->core->ops;
 438        int ret;
 439
 440        if (inst->state < INST_INIT || inst->state >= INST_STOP)
 441                return -EINVAL;
 442
 443        reinit_completion(&inst->done);
 444
 445        ret = ops->session_get_property(inst, ptype);
 446        if (ret)
 447                return ret;
 448
 449        ret = wait_session_msg(inst);
 450        if (ret)
 451                return ret;
 452
 453        *hprop = inst->hprop;
 454
 455        return 0;
 456}
 457EXPORT_SYMBOL_GPL(hfi_session_get_property);
 458
 459int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata)
 460{
 461        const struct hfi_ops *ops = inst->core->ops;
 462
 463        if (inst->state < INST_INIT || inst->state >= INST_STOP)
 464                return -EINVAL;
 465
 466        return ops->session_set_property(inst, ptype, pdata);
 467}
 468EXPORT_SYMBOL_GPL(hfi_session_set_property);
 469
 470int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd)
 471{
 472        const struct hfi_ops *ops = inst->core->ops;
 473
 474        if (fd->buffer_type == HFI_BUFFER_INPUT)
 475                return ops->session_etb(inst, fd);
 476        else if (fd->buffer_type == HFI_BUFFER_OUTPUT)
 477                return ops->session_ftb(inst, fd);
 478
 479        return -EINVAL;
 480}
 481EXPORT_SYMBOL_GPL(hfi_session_process_buf);
 482
 483irqreturn_t hfi_isr_thread(int irq, void *dev_id)
 484{
 485        struct venus_core *core = dev_id;
 486
 487        return core->ops->isr_thread(core);
 488}
 489
 490irqreturn_t hfi_isr(int irq, void *dev)
 491{
 492        struct venus_core *core = dev;
 493
 494        return core->ops->isr(core);
 495}
 496
 497int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
 498{
 499        int ret;
 500
 501        if (!ops)
 502                return -EINVAL;
 503
 504        atomic_set(&core->insts_count, 0);
 505        core->core_ops = ops;
 506        core->state = CORE_UNINIT;
 507        init_completion(&core->done);
 508        pkt_set_version(core->res->hfi_version);
 509        ret = venus_hfi_create(core);
 510
 511        return ret;
 512}
 513
 514void hfi_destroy(struct venus_core *core)
 515{
 516        venus_hfi_destroy(core);
 517}
 518