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