linux/drivers/media/platform/vsp1/vsp1_drm.c
<<
>>
Prefs
   1/*
   2 * vsp1_drm.c  --  R-Car VSP1 DRM API
   3 *
   4 * Copyright (C) 2015 Renesas Electronics Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/device.h>
  15#include <linux/slab.h>
  16
  17#include <media/media-entity.h>
  18#include <media/v4l2-subdev.h>
  19#include <media/vsp1.h>
  20
  21#include "vsp1.h"
  22#include "vsp1_bru.h"
  23#include "vsp1_dl.h"
  24#include "vsp1_drm.h"
  25#include "vsp1_lif.h"
  26#include "vsp1_pipe.h"
  27#include "vsp1_rwpf.h"
  28
  29
  30/* -----------------------------------------------------------------------------
  31 * Interrupt Handling
  32 */
  33
  34void vsp1_drm_display_start(struct vsp1_device *vsp1)
  35{
  36        vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm);
  37}
  38
  39/* -----------------------------------------------------------------------------
  40 * DU Driver API
  41 */
  42
  43int vsp1_du_init(struct device *dev)
  44{
  45        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
  46
  47        if (!vsp1)
  48                return -EPROBE_DEFER;
  49
  50        return 0;
  51}
  52EXPORT_SYMBOL_GPL(vsp1_du_init);
  53
  54/**
  55 * vsp1_du_setup_lif - Setup the output part of the VSP pipeline
  56 * @dev: the VSP device
  57 * @width: output frame width in pixels
  58 * @height: output frame height in pixels
  59 *
  60 * Configure the output part of VSP DRM pipeline for the given frame @width and
  61 * @height. This sets up formats on the BRU source pad, the WPF0 sink and source
  62 * pads, and the LIF sink pad.
  63 *
  64 * As the media bus code on the BRU source pad is conditioned by the
  65 * configuration of the BRU sink 0 pad, we also set up the formats on all BRU
  66 * sinks, even if the configuration will be overwritten later by
  67 * vsp1_du_setup_rpf(). This ensures that the BRU configuration is set to a well
  68 * defined state.
  69 *
  70 * Return 0 on success or a negative error code on failure.
  71 */
  72int vsp1_du_setup_lif(struct device *dev, unsigned int width,
  73                      unsigned int height)
  74{
  75        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
  76        struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
  77        struct vsp1_bru *bru = vsp1->bru;
  78        struct v4l2_subdev_format format;
  79        unsigned int i;
  80        int ret;
  81
  82        dev_dbg(vsp1->dev, "%s: configuring LIF with format %ux%u\n",
  83                __func__, width, height);
  84
  85        if (width == 0 || height == 0) {
  86                /* Zero width or height means the CRTC is being disabled, stop
  87                 * the pipeline and turn the light off.
  88                 */
  89                ret = vsp1_pipeline_stop(pipe);
  90                if (ret == -ETIMEDOUT)
  91                        dev_err(vsp1->dev, "DRM pipeline stop timeout\n");
  92
  93                media_entity_pipeline_stop(&pipe->output->entity.subdev.entity);
  94
  95                for (i = 0; i < bru->entity.source_pad; ++i) {
  96                        vsp1->drm->inputs[i].enabled = false;
  97                        bru->inputs[i].rpf = NULL;
  98                        pipe->inputs[i] = NULL;
  99                }
 100
 101                pipe->num_inputs = 0;
 102
 103                vsp1_dlm_reset(pipe->output->dlm);
 104                vsp1_device_put(vsp1);
 105
 106                dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__);
 107
 108                return 0;
 109        }
 110
 111        /* Configure the format at the BRU sinks and propagate it through the
 112         * pipeline.
 113         */
 114        memset(&format, 0, sizeof(format));
 115        format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 116
 117        for (i = 0; i < bru->entity.source_pad; ++i) {
 118                format.pad = i;
 119
 120                format.format.width = width;
 121                format.format.height = height;
 122                format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
 123                format.format.field = V4L2_FIELD_NONE;
 124
 125                ret = v4l2_subdev_call(&bru->entity.subdev, pad,
 126                                       set_fmt, NULL, &format);
 127                if (ret < 0)
 128                        return ret;
 129
 130                dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
 131                        __func__, format.format.width, format.format.height,
 132                        format.format.code, i);
 133        }
 134
 135        format.pad = bru->entity.source_pad;
 136        format.format.width = width;
 137        format.format.height = height;
 138        format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
 139        format.format.field = V4L2_FIELD_NONE;
 140
 141        ret = v4l2_subdev_call(&bru->entity.subdev, pad, set_fmt, NULL,
 142                               &format);
 143        if (ret < 0)
 144                return ret;
 145
 146        dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
 147                __func__, format.format.width, format.format.height,
 148                format.format.code, i);
 149
 150        format.pad = RWPF_PAD_SINK;
 151        ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, set_fmt, NULL,
 152                               &format);
 153        if (ret < 0)
 154                return ret;
 155
 156        dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF0 sink\n",
 157                __func__, format.format.width, format.format.height,
 158                format.format.code);
 159
 160        format.pad = RWPF_PAD_SOURCE;
 161        ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, get_fmt, NULL,
 162                               &format);
 163        if (ret < 0)
 164                return ret;
 165
 166        dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF0 source\n",
 167                __func__, format.format.width, format.format.height,
 168                format.format.code);
 169
 170        format.pad = LIF_PAD_SINK;
 171        ret = v4l2_subdev_call(&vsp1->lif->entity.subdev, pad, set_fmt, NULL,
 172                               &format);
 173        if (ret < 0)
 174                return ret;
 175
 176        dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF sink\n",
 177                __func__, format.format.width, format.format.height,
 178                format.format.code);
 179
 180        /* Verify that the format at the output of the pipeline matches the
 181         * requested frame size and media bus code.
 182         */
 183        if (format.format.width != width || format.format.height != height ||
 184            format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) {
 185                dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__);
 186                return -EPIPE;
 187        }
 188
 189        /* Mark the pipeline as streaming and enable the VSP1. This will store
 190         * the pipeline pointer in all entities, which the s_stream handlers
 191         * will need. We don't start the entities themselves right at this point
 192         * as there's no plane configured yet, so we can't start processing
 193         * buffers.
 194         */
 195        ret = vsp1_device_get(vsp1);
 196        if (ret < 0)
 197                return ret;
 198
 199        ret = media_entity_pipeline_start(&pipe->output->entity.subdev.entity,
 200                                          &pipe->pipe);
 201        if (ret < 0) {
 202                dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__);
 203                vsp1_device_put(vsp1);
 204                return ret;
 205        }
 206
 207        dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__);
 208
 209        return 0;
 210}
 211EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
 212
 213/**
 214 * vsp1_du_atomic_begin - Prepare for an atomic update
 215 * @dev: the VSP device
 216 */
 217void vsp1_du_atomic_begin(struct device *dev)
 218{
 219        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 220        struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
 221
 222        vsp1->drm->num_inputs = pipe->num_inputs;
 223
 224        /* Prepare the display list. */
 225        pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
 226}
 227EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
 228
 229/**
 230 * vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline
 231 * @dev: the VSP device
 232 * @rpf_index: index of the RPF to setup (0-based)
 233 * @cfg: the RPF configuration
 234 *
 235 * Configure the VSP to perform image composition through RPF @rpf_index as
 236 * described by the @cfg configuration. The image to compose is referenced by
 237 * @cfg.mem and composed using the @cfg.src crop rectangle and the @cfg.dst
 238 * composition rectangle. The Z-order is configurable with higher @zpos values
 239 * displayed on top.
 240 *
 241 * If the @cfg configuration is NULL, the RPF will be disabled. Calling the
 242 * function on a disabled RPF is allowed.
 243 *
 244 * Image format as stored in memory is expressed as a V4L2 @cfg.pixelformat
 245 * value. The memory pitch is configurable to allow for padding at end of lines,
 246 * or simply for images that extend beyond the crop rectangle boundaries. The
 247 * @cfg.pitch value is expressed in bytes and applies to all planes for
 248 * multiplanar formats.
 249 *
 250 * The source memory buffer is referenced by the DMA address of its planes in
 251 * the @cfg.mem array. Up to two planes are supported. The second plane DMA
 252 * address is ignored for formats using a single plane.
 253 *
 254 * This function isn't reentrant, the caller needs to serialize calls.
 255 *
 256 * Return 0 on success or a negative error code on failure.
 257 */
 258int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
 259                          const struct vsp1_du_atomic_config *cfg)
 260{
 261        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 262        const struct vsp1_format_info *fmtinfo;
 263        struct vsp1_rwpf *rpf;
 264
 265        if (rpf_index >= vsp1->info->rpf_count)
 266                return -EINVAL;
 267
 268        rpf = vsp1->rpf[rpf_index];
 269
 270        if (!cfg) {
 271                dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__,
 272                        rpf_index);
 273
 274                vsp1->drm->inputs[rpf_index].enabled = false;
 275                return 0;
 276        }
 277
 278        dev_dbg(vsp1->dev,
 279                "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n",
 280                __func__, rpf_index,
 281                cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height,
 282                cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height,
 283                cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
 284                &cfg->mem[2], cfg->zpos);
 285
 286        /*
 287         * Store the format, stride, memory buffer address, crop and compose
 288         * rectangles and Z-order position and for the input.
 289         */
 290        fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat);
 291        if (!fmtinfo) {
 292                dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
 293                        cfg->pixelformat);
 294                return -EINVAL;
 295        }
 296
 297        rpf->fmtinfo = fmtinfo;
 298        rpf->format.num_planes = fmtinfo->planes;
 299        rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
 300        rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
 301        rpf->alpha = cfg->alpha;
 302
 303        rpf->mem.addr[0] = cfg->mem[0];
 304        rpf->mem.addr[1] = cfg->mem[1];
 305        rpf->mem.addr[2] = cfg->mem[2];
 306
 307        vsp1->drm->inputs[rpf_index].crop = cfg->src;
 308        vsp1->drm->inputs[rpf_index].compose = cfg->dst;
 309        vsp1->drm->inputs[rpf_index].zpos = cfg->zpos;
 310        vsp1->drm->inputs[rpf_index].enabled = true;
 311
 312        return 0;
 313}
 314EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
 315
 316static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
 317                                  struct vsp1_rwpf *rpf, unsigned int bru_input)
 318{
 319        struct v4l2_subdev_selection sel;
 320        struct v4l2_subdev_format format;
 321        const struct v4l2_rect *crop;
 322        int ret;
 323
 324        /* Configure the format on the RPF sink pad and propagate it up to the
 325         * BRU sink pad.
 326         */
 327        crop = &vsp1->drm->inputs[rpf->entity.index].crop;
 328
 329        memset(&format, 0, sizeof(format));
 330        format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 331        format.pad = RWPF_PAD_SINK;
 332        format.format.width = crop->width + crop->left;
 333        format.format.height = crop->height + crop->top;
 334        format.format.code = rpf->fmtinfo->mbus;
 335        format.format.field = V4L2_FIELD_NONE;
 336
 337        ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
 338                               &format);
 339        if (ret < 0)
 340                return ret;
 341
 342        dev_dbg(vsp1->dev,
 343                "%s: set format %ux%u (%x) on RPF%u sink\n",
 344                __func__, format.format.width, format.format.height,
 345                format.format.code, rpf->entity.index);
 346
 347        memset(&sel, 0, sizeof(sel));
 348        sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 349        sel.pad = RWPF_PAD_SINK;
 350        sel.target = V4L2_SEL_TGT_CROP;
 351        sel.r = *crop;
 352
 353        ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
 354                               &sel);
 355        if (ret < 0)
 356                return ret;
 357
 358        dev_dbg(vsp1->dev,
 359                "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n",
 360                __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
 361                rpf->entity.index);
 362
 363        /* RPF source, hardcode the format to ARGB8888 to turn on format
 364         * conversion if needed.
 365         */
 366        format.pad = RWPF_PAD_SOURCE;
 367
 368        ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL,
 369                               &format);
 370        if (ret < 0)
 371                return ret;
 372
 373        dev_dbg(vsp1->dev,
 374                "%s: got format %ux%u (%x) on RPF%u source\n",
 375                __func__, format.format.width, format.format.height,
 376                format.format.code, rpf->entity.index);
 377
 378        format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
 379
 380        ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
 381                               &format);
 382        if (ret < 0)
 383                return ret;
 384
 385        /* BRU sink, propagate the format from the RPF source. */
 386        format.pad = bru_input;
 387
 388        ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL,
 389                               &format);
 390        if (ret < 0)
 391                return ret;
 392
 393        dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
 394                __func__, format.format.width, format.format.height,
 395                format.format.code, format.pad);
 396
 397        sel.pad = bru_input;
 398        sel.target = V4L2_SEL_TGT_COMPOSE;
 399        sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
 400
 401        ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_selection,
 402                               NULL, &sel);
 403        if (ret < 0)
 404                return ret;
 405
 406        dev_dbg(vsp1->dev,
 407                "%s: set selection (%u,%u)/%ux%u on BRU pad %u\n",
 408                __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
 409                sel.pad);
 410
 411        return 0;
 412}
 413
 414static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
 415{
 416        return vsp1->drm->inputs[rpf->entity.index].zpos;
 417}
 418
 419/**
 420 * vsp1_du_atomic_flush - Commit an atomic update
 421 * @dev: the VSP device
 422 */
 423void vsp1_du_atomic_flush(struct device *dev)
 424{
 425        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 426        struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
 427        struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
 428        struct vsp1_entity *entity;
 429        unsigned long flags;
 430        unsigned int i;
 431        int ret;
 432
 433        /* Count the number of enabled inputs and sort them by Z-order. */
 434        pipe->num_inputs = 0;
 435
 436        for (i = 0; i < vsp1->info->rpf_count; ++i) {
 437                struct vsp1_rwpf *rpf = vsp1->rpf[i];
 438                unsigned int j;
 439
 440                if (!vsp1->drm->inputs[i].enabled) {
 441                        pipe->inputs[i] = NULL;
 442                        continue;
 443                }
 444
 445                pipe->inputs[i] = rpf;
 446
 447                /* Insert the RPF in the sorted RPFs array. */
 448                for (j = pipe->num_inputs++; j > 0; --j) {
 449                        if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
 450                                break;
 451                        inputs[j] = inputs[j-1];
 452                }
 453
 454                inputs[j] = rpf;
 455        }
 456
 457        /* Setup the RPF input pipeline for every enabled input. */
 458        for (i = 0; i < vsp1->info->num_bru_inputs; ++i) {
 459                struct vsp1_rwpf *rpf = inputs[i];
 460
 461                if (!rpf) {
 462                        vsp1->bru->inputs[i].rpf = NULL;
 463                        continue;
 464                }
 465
 466                vsp1->bru->inputs[i].rpf = rpf;
 467                rpf->bru_input = i;
 468                rpf->entity.sink_pad = i;
 469
 470                dev_dbg(vsp1->dev, "%s: connecting RPF.%u to BRU:%u\n",
 471                        __func__, rpf->entity.index, i);
 472
 473                ret = vsp1_du_setup_rpf_pipe(vsp1, rpf, i);
 474                if (ret < 0)
 475                        dev_err(vsp1->dev,
 476                                "%s: failed to setup RPF.%u\n",
 477                                __func__, rpf->entity.index);
 478        }
 479
 480        /* Configure all entities in the pipeline. */
 481        list_for_each_entry(entity, &pipe->entities, list_pipe) {
 482                /* Disconnect unused RPFs from the pipeline. */
 483                if (entity->type == VSP1_ENTITY_RPF) {
 484                        struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
 485
 486                        if (!pipe->inputs[rpf->entity.index]) {
 487                                vsp1_dl_list_write(pipe->dl, entity->route->reg,
 488                                                   VI6_DPR_NODE_UNUSED);
 489                                continue;
 490                        }
 491                }
 492
 493                vsp1_entity_route_setup(entity, pipe->dl);
 494
 495                if (entity->ops->configure) {
 496                        entity->ops->configure(entity, pipe, pipe->dl,
 497                                               VSP1_ENTITY_PARAMS_INIT);
 498                        entity->ops->configure(entity, pipe, pipe->dl,
 499                                               VSP1_ENTITY_PARAMS_RUNTIME);
 500                        entity->ops->configure(entity, pipe, pipe->dl,
 501                                               VSP1_ENTITY_PARAMS_PARTITION);
 502                }
 503        }
 504
 505        vsp1_dl_list_commit(pipe->dl);
 506        pipe->dl = NULL;
 507
 508        /* Start or stop the pipeline if needed. */
 509        if (!vsp1->drm->num_inputs && pipe->num_inputs) {
 510                vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
 511                vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE);
 512                spin_lock_irqsave(&pipe->irqlock, flags);
 513                vsp1_pipeline_run(pipe);
 514                spin_unlock_irqrestore(&pipe->irqlock, flags);
 515        } else if (vsp1->drm->num_inputs && !pipe->num_inputs) {
 516                vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
 517                vsp1_pipeline_stop(pipe);
 518        }
 519}
 520EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
 521
 522/* -----------------------------------------------------------------------------
 523 * Initialization
 524 */
 525
 526int vsp1_drm_create_links(struct vsp1_device *vsp1)
 527{
 528        const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
 529        unsigned int i;
 530        int ret;
 531
 532        /* VSPD instances require a BRU to perform composition and a LIF to
 533         * output to the DU.
 534         */
 535        if (!vsp1->bru || !vsp1->lif)
 536                return -ENXIO;
 537
 538        for (i = 0; i < vsp1->info->rpf_count; ++i) {
 539                struct vsp1_rwpf *rpf = vsp1->rpf[i];
 540
 541                ret = media_create_pad_link(&rpf->entity.subdev.entity,
 542                                            RWPF_PAD_SOURCE,
 543                                            &vsp1->bru->entity.subdev.entity,
 544                                            i, flags);
 545                if (ret < 0)
 546                        return ret;
 547
 548                rpf->entity.sink = &vsp1->bru->entity.subdev.entity;
 549                rpf->entity.sink_pad = i;
 550        }
 551
 552        ret = media_create_pad_link(&vsp1->bru->entity.subdev.entity,
 553                                    vsp1->bru->entity.source_pad,
 554                                    &vsp1->wpf[0]->entity.subdev.entity,
 555                                    RWPF_PAD_SINK, flags);
 556        if (ret < 0)
 557                return ret;
 558
 559        vsp1->bru->entity.sink = &vsp1->wpf[0]->entity.subdev.entity;
 560        vsp1->bru->entity.sink_pad = RWPF_PAD_SINK;
 561
 562        ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
 563                                    RWPF_PAD_SOURCE,
 564                                    &vsp1->lif->entity.subdev.entity,
 565                                    LIF_PAD_SINK, flags);
 566        if (ret < 0)
 567                return ret;
 568
 569        return 0;
 570}
 571
 572int vsp1_drm_init(struct vsp1_device *vsp1)
 573{
 574        struct vsp1_pipeline *pipe;
 575        unsigned int i;
 576
 577        vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL);
 578        if (!vsp1->drm)
 579                return -ENOMEM;
 580
 581        pipe = &vsp1->drm->pipe;
 582
 583        vsp1_pipeline_init(pipe);
 584
 585        /* The DRM pipeline is static, add entities manually. */
 586        for (i = 0; i < vsp1->info->rpf_count; ++i) {
 587                struct vsp1_rwpf *input = vsp1->rpf[i];
 588
 589                list_add_tail(&input->entity.list_pipe, &pipe->entities);
 590        }
 591
 592        list_add_tail(&vsp1->bru->entity.list_pipe, &pipe->entities);
 593        list_add_tail(&vsp1->wpf[0]->entity.list_pipe, &pipe->entities);
 594        list_add_tail(&vsp1->lif->entity.list_pipe, &pipe->entities);
 595
 596        pipe->bru = &vsp1->bru->entity;
 597        pipe->lif = &vsp1->lif->entity;
 598        pipe->output = vsp1->wpf[0];
 599
 600        return 0;
 601}
 602
 603void vsp1_drm_cleanup(struct vsp1_device *vsp1)
 604{
 605}
 606