linux/drivers/media/platform/qcom/venus/core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
   4 * Copyright (C) 2017 Linaro Ltd.
   5 */
   6#include <linux/clk.h>
   7#include <linux/init.h>
   8#include <linux/ioctl.h>
   9#include <linux/list.h>
  10#include <linux/module.h>
  11#include <linux/of_device.h>
  12#include <linux/platform_device.h>
  13#include <linux/slab.h>
  14#include <linux/types.h>
  15#include <linux/pm_runtime.h>
  16#include <media/videobuf2-v4l2.h>
  17#include <media/v4l2-mem2mem.h>
  18#include <media/v4l2-ioctl.h>
  19
  20#include "core.h"
  21#include "vdec.h"
  22#include "venc.h"
  23#include "firmware.h"
  24
  25static void venus_event_notify(struct venus_core *core, u32 event)
  26{
  27        struct venus_inst *inst;
  28
  29        switch (event) {
  30        case EVT_SYS_WATCHDOG_TIMEOUT:
  31        case EVT_SYS_ERROR:
  32                break;
  33        default:
  34                return;
  35        }
  36
  37        mutex_lock(&core->lock);
  38        core->sys_error = true;
  39        list_for_each_entry(inst, &core->instances, list)
  40                inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
  41        mutex_unlock(&core->lock);
  42
  43        disable_irq_nosync(core->irq);
  44
  45        /*
  46         * Delay recovery to ensure venus has completed any pending cache
  47         * operations. Without this sleep, we see device reset when firmware is
  48         * unloaded after a system error.
  49         */
  50        schedule_delayed_work(&core->work, msecs_to_jiffies(100));
  51}
  52
  53static const struct hfi_core_ops venus_core_ops = {
  54        .event_notify = venus_event_notify,
  55};
  56
  57static void venus_sys_error_handler(struct work_struct *work)
  58{
  59        struct venus_core *core =
  60                        container_of(work, struct venus_core, work.work);
  61        int ret = 0;
  62
  63        dev_warn(core->dev, "system error has occurred, starting recovery!\n");
  64
  65        pm_runtime_get_sync(core->dev);
  66
  67        hfi_core_deinit(core, true);
  68        hfi_destroy(core);
  69        mutex_lock(&core->lock);
  70        venus_shutdown(core);
  71
  72        pm_runtime_put_sync(core->dev);
  73
  74        ret |= hfi_create(core, &venus_core_ops);
  75
  76        pm_runtime_get_sync(core->dev);
  77
  78        ret |= venus_boot(core);
  79
  80        ret |= hfi_core_resume(core, true);
  81
  82        enable_irq(core->irq);
  83
  84        mutex_unlock(&core->lock);
  85
  86        ret |= hfi_core_init(core);
  87
  88        pm_runtime_put_sync(core->dev);
  89
  90        if (ret) {
  91                disable_irq_nosync(core->irq);
  92                dev_warn(core->dev, "recovery failed (%d)\n", ret);
  93                schedule_delayed_work(&core->work, msecs_to_jiffies(10));
  94                return;
  95        }
  96
  97        mutex_lock(&core->lock);
  98        core->sys_error = false;
  99        mutex_unlock(&core->lock);
 100}
 101
 102static int venus_clks_get(struct venus_core *core)
 103{
 104        const struct venus_resources *res = core->res;
 105        struct device *dev = core->dev;
 106        unsigned int i;
 107
 108        for (i = 0; i < res->clks_num; i++) {
 109                core->clks[i] = devm_clk_get(dev, res->clks[i]);
 110                if (IS_ERR(core->clks[i]))
 111                        return PTR_ERR(core->clks[i]);
 112        }
 113
 114        return 0;
 115}
 116
 117static int venus_clks_enable(struct venus_core *core)
 118{
 119        const struct venus_resources *res = core->res;
 120        unsigned int i;
 121        int ret;
 122
 123        for (i = 0; i < res->clks_num; i++) {
 124                ret = clk_prepare_enable(core->clks[i]);
 125                if (ret)
 126                        goto err;
 127        }
 128
 129        return 0;
 130err:
 131        while (i--)
 132                clk_disable_unprepare(core->clks[i]);
 133
 134        return ret;
 135}
 136
 137static void venus_clks_disable(struct venus_core *core)
 138{
 139        const struct venus_resources *res = core->res;
 140        unsigned int i = res->clks_num;
 141
 142        while (i--)
 143                clk_disable_unprepare(core->clks[i]);
 144}
 145
 146static u32 to_v4l2_codec_type(u32 codec)
 147{
 148        switch (codec) {
 149        case HFI_VIDEO_CODEC_H264:
 150                return V4L2_PIX_FMT_H264;
 151        case HFI_VIDEO_CODEC_H263:
 152                return V4L2_PIX_FMT_H263;
 153        case HFI_VIDEO_CODEC_MPEG1:
 154                return V4L2_PIX_FMT_MPEG1;
 155        case HFI_VIDEO_CODEC_MPEG2:
 156                return V4L2_PIX_FMT_MPEG2;
 157        case HFI_VIDEO_CODEC_MPEG4:
 158                return V4L2_PIX_FMT_MPEG4;
 159        case HFI_VIDEO_CODEC_VC1:
 160                return V4L2_PIX_FMT_VC1_ANNEX_G;
 161        case HFI_VIDEO_CODEC_VP8:
 162                return V4L2_PIX_FMT_VP8;
 163        case HFI_VIDEO_CODEC_VP9:
 164                return V4L2_PIX_FMT_VP9;
 165        case HFI_VIDEO_CODEC_DIVX:
 166        case HFI_VIDEO_CODEC_DIVX_311:
 167                return V4L2_PIX_FMT_XVID;
 168        default:
 169                return 0;
 170        }
 171}
 172
 173static int venus_enumerate_codecs(struct venus_core *core, u32 type)
 174{
 175        const struct hfi_inst_ops dummy_ops = {};
 176        struct venus_inst *inst;
 177        u32 codec, codecs;
 178        unsigned int i;
 179        int ret;
 180
 181        if (core->res->hfi_version != HFI_VERSION_1XX)
 182                return 0;
 183
 184        inst = kzalloc(sizeof(*inst), GFP_KERNEL);
 185        if (!inst)
 186                return -ENOMEM;
 187
 188        mutex_init(&inst->lock);
 189        inst->core = core;
 190        inst->session_type = type;
 191        if (type == VIDC_SESSION_TYPE_DEC)
 192                codecs = core->dec_codecs;
 193        else
 194                codecs = core->enc_codecs;
 195
 196        ret = hfi_session_create(inst, &dummy_ops);
 197        if (ret)
 198                goto err;
 199
 200        for (i = 0; i < MAX_CODEC_NUM; i++) {
 201                codec = (1 << i) & codecs;
 202                if (!codec)
 203                        continue;
 204
 205                ret = hfi_session_init(inst, to_v4l2_codec_type(codec));
 206                if (ret)
 207                        goto done;
 208
 209                ret = hfi_session_deinit(inst);
 210                if (ret)
 211                        goto done;
 212        }
 213
 214done:
 215        hfi_session_destroy(inst);
 216err:
 217        mutex_destroy(&inst->lock);
 218        kfree(inst);
 219
 220        return ret;
 221}
 222
 223static int venus_probe(struct platform_device *pdev)
 224{
 225        struct device *dev = &pdev->dev;
 226        struct venus_core *core;
 227        struct resource *r;
 228        int ret;
 229
 230        core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
 231        if (!core)
 232                return -ENOMEM;
 233
 234        core->dev = dev;
 235        platform_set_drvdata(pdev, core);
 236
 237        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 238        core->base = devm_ioremap_resource(dev, r);
 239        if (IS_ERR(core->base))
 240                return PTR_ERR(core->base);
 241
 242        core->irq = platform_get_irq(pdev, 0);
 243        if (core->irq < 0)
 244                return core->irq;
 245
 246        core->res = of_device_get_match_data(dev);
 247        if (!core->res)
 248                return -ENODEV;
 249
 250        ret = venus_clks_get(core);
 251        if (ret)
 252                return ret;
 253
 254        ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
 255        if (ret)
 256                return ret;
 257
 258        if (!dev->dma_parms) {
 259                dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
 260                                              GFP_KERNEL);
 261                if (!dev->dma_parms)
 262                        return -ENOMEM;
 263        }
 264        dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
 265
 266        INIT_LIST_HEAD(&core->instances);
 267        mutex_init(&core->lock);
 268        INIT_DELAYED_WORK(&core->work, venus_sys_error_handler);
 269
 270        ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, hfi_isr_thread,
 271                                        IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
 272                                        "venus", core);
 273        if (ret)
 274                return ret;
 275
 276        ret = hfi_create(core, &venus_core_ops);
 277        if (ret)
 278                return ret;
 279
 280        pm_runtime_enable(dev);
 281
 282        ret = pm_runtime_get_sync(dev);
 283        if (ret < 0)
 284                goto err_runtime_disable;
 285
 286        ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
 287        if (ret)
 288                goto err_runtime_disable;
 289
 290        ret = venus_firmware_init(core);
 291        if (ret)
 292                goto err_runtime_disable;
 293
 294        ret = venus_boot(core);
 295        if (ret)
 296                goto err_runtime_disable;
 297
 298        ret = hfi_core_resume(core, true);
 299        if (ret)
 300                goto err_venus_shutdown;
 301
 302        ret = hfi_core_init(core);
 303        if (ret)
 304                goto err_venus_shutdown;
 305
 306        ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC);
 307        if (ret)
 308                goto err_venus_shutdown;
 309
 310        ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC);
 311        if (ret)
 312                goto err_venus_shutdown;
 313
 314        ret = v4l2_device_register(dev, &core->v4l2_dev);
 315        if (ret)
 316                goto err_core_deinit;
 317
 318        ret = pm_runtime_put_sync(dev);
 319        if (ret)
 320                goto err_dev_unregister;
 321
 322        return 0;
 323
 324err_dev_unregister:
 325        v4l2_device_unregister(&core->v4l2_dev);
 326err_core_deinit:
 327        hfi_core_deinit(core, false);
 328err_venus_shutdown:
 329        venus_shutdown(core);
 330err_runtime_disable:
 331        pm_runtime_set_suspended(dev);
 332        pm_runtime_disable(dev);
 333        hfi_destroy(core);
 334        return ret;
 335}
 336
 337static int venus_remove(struct platform_device *pdev)
 338{
 339        struct venus_core *core = platform_get_drvdata(pdev);
 340        struct device *dev = core->dev;
 341        int ret;
 342
 343        ret = pm_runtime_get_sync(dev);
 344        WARN_ON(ret < 0);
 345
 346        ret = hfi_core_deinit(core, true);
 347        WARN_ON(ret);
 348
 349        hfi_destroy(core);
 350        venus_shutdown(core);
 351        of_platform_depopulate(dev);
 352
 353        venus_firmware_deinit(core);
 354
 355        pm_runtime_put_sync(dev);
 356        pm_runtime_disable(dev);
 357
 358        v4l2_device_unregister(&core->v4l2_dev);
 359
 360        return ret;
 361}
 362
 363static __maybe_unused int venus_runtime_suspend(struct device *dev)
 364{
 365        struct venus_core *core = dev_get_drvdata(dev);
 366        int ret;
 367
 368        ret = hfi_core_suspend(core);
 369
 370        venus_clks_disable(core);
 371
 372        return ret;
 373}
 374
 375static __maybe_unused int venus_runtime_resume(struct device *dev)
 376{
 377        struct venus_core *core = dev_get_drvdata(dev);
 378        int ret;
 379
 380        ret = venus_clks_enable(core);
 381        if (ret)
 382                return ret;
 383
 384        ret = hfi_core_resume(core, false);
 385        if (ret)
 386                goto err_clks_disable;
 387
 388        return 0;
 389
 390err_clks_disable:
 391        venus_clks_disable(core);
 392        return ret;
 393}
 394
 395static const struct dev_pm_ops venus_pm_ops = {
 396        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 397                                pm_runtime_force_resume)
 398        SET_RUNTIME_PM_OPS(venus_runtime_suspend, venus_runtime_resume, NULL)
 399};
 400
 401static const struct freq_tbl msm8916_freq_table[] = {
 402        { 352800, 228570000 },  /* 1920x1088 @ 30 + 1280x720 @ 30 */
 403        { 244800, 160000000 },  /* 1920x1088 @ 30 */
 404        { 108000, 100000000 },  /* 1280x720 @ 30 */
 405};
 406
 407static const struct reg_val msm8916_reg_preset[] = {
 408        { 0xe0020, 0x05555556 },
 409        { 0xe0024, 0x05555556 },
 410        { 0x80124, 0x00000003 },
 411};
 412
 413static const struct venus_resources msm8916_res = {
 414        .freq_tbl = msm8916_freq_table,
 415        .freq_tbl_size = ARRAY_SIZE(msm8916_freq_table),
 416        .reg_tbl = msm8916_reg_preset,
 417        .reg_tbl_size = ARRAY_SIZE(msm8916_reg_preset),
 418        .clks = { "core", "iface", "bus", },
 419        .clks_num = 3,
 420        .max_load = 352800, /* 720p@30 + 1080p@30 */
 421        .hfi_version = HFI_VERSION_1XX,
 422        .vmem_id = VIDC_RESOURCE_NONE,
 423        .vmem_size = 0,
 424        .vmem_addr = 0,
 425        .dma_mask = 0xddc00000 - 1,
 426        .fwname = "qcom/venus-1.8/venus.mdt",
 427};
 428
 429static const struct freq_tbl msm8996_freq_table[] = {
 430        { 1944000, 490000000 }, /* 4k UHD @ 60 */
 431        {  972000, 320000000 }, /* 4k UHD @ 30 */
 432        {  489600, 150000000 }, /* 1080p @ 60 */
 433        {  244800,  75000000 }, /* 1080p @ 30 */
 434};
 435
 436static const struct reg_val msm8996_reg_preset[] = {
 437        { 0x80010, 0xffffffff },
 438        { 0x80018, 0x00001556 },
 439        { 0x8001C, 0x00001556 },
 440};
 441
 442static const struct venus_resources msm8996_res = {
 443        .freq_tbl = msm8996_freq_table,
 444        .freq_tbl_size = ARRAY_SIZE(msm8996_freq_table),
 445        .reg_tbl = msm8996_reg_preset,
 446        .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset),
 447        .clks = {"core", "iface", "bus", "mbus" },
 448        .clks_num = 4,
 449        .max_load = 2563200,
 450        .hfi_version = HFI_VERSION_3XX,
 451        .vmem_id = VIDC_RESOURCE_NONE,
 452        .vmem_size = 0,
 453        .vmem_addr = 0,
 454        .dma_mask = 0xddc00000 - 1,
 455        .fwname = "qcom/venus-4.2/venus.mdt",
 456};
 457
 458static const struct freq_tbl sdm845_freq_table[] = {
 459        { 3110400, 533000000 }, /* 4096x2160@90 */
 460        { 2073600, 444000000 }, /* 4096x2160@60 */
 461        { 1944000, 404000000 }, /* 3840x2160@60 */
 462        {  972000, 330000000 }, /* 3840x2160@30 */
 463        {  489600, 200000000 }, /* 1920x1080@60 */
 464        {  244800, 100000000 }, /* 1920x1080@30 */
 465};
 466
 467static const struct venus_resources sdm845_res = {
 468        .freq_tbl = sdm845_freq_table,
 469        .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table),
 470        .clks = {"core", "iface", "bus" },
 471        .clks_num = 3,
 472        .max_load = 3110400,    /* 4096x2160@90 */
 473        .hfi_version = HFI_VERSION_4XX,
 474        .vmem_id = VIDC_RESOURCE_NONE,
 475        .vmem_size = 0,
 476        .vmem_addr = 0,
 477        .dma_mask = 0xe0000000 - 1,
 478        .fwname = "qcom/venus-5.2/venus.mdt",
 479};
 480
 481static const struct of_device_id venus_dt_match[] = {
 482        { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
 483        { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
 484        { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
 485        { }
 486};
 487MODULE_DEVICE_TABLE(of, venus_dt_match);
 488
 489static struct platform_driver qcom_venus_driver = {
 490        .probe = venus_probe,
 491        .remove = venus_remove,
 492        .driver = {
 493                .name = "qcom-venus",
 494                .of_match_table = venus_dt_match,
 495                .pm = &venus_pm_ops,
 496        },
 497};
 498module_platform_driver(qcom_venus_driver);
 499
 500MODULE_ALIAS("platform:qcom-venus");
 501MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver");
 502MODULE_LICENSE("GPL v2");
 503