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