• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
vsp1_drm_display_start(struct vsp1_device * vsp1)34 void 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 
vsp1_du_init(struct device * dev)43 int 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 }
52 EXPORT_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  */
vsp1_du_setup_lif(struct device * dev,unsigned int width,unsigned int height)72 int 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 }
211 EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
212 
213 /**
214  * vsp1_du_atomic_begin - Prepare for an atomic update
215  * @dev: the VSP device
216  */
vsp1_du_atomic_begin(struct device * dev)217 void 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 }
227 EXPORT_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  */
vsp1_du_atomic_update(struct device * dev,unsigned int rpf_index,const struct vsp1_du_atomic_config * cfg)258 int 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 }
314 EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
315 
vsp1_du_setup_rpf_pipe(struct vsp1_device * vsp1,struct vsp1_rwpf * rpf,unsigned int bru_input)316 static 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 
rpf_zpos(struct vsp1_device * vsp1,struct vsp1_rwpf * rpf)414 static 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  */
vsp1_du_atomic_flush(struct device * dev)423 void 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 }
520 EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
521 
522 /* -----------------------------------------------------------------------------
523  * Initialization
524  */
525 
vsp1_drm_create_links(struct vsp1_device * vsp1)526 int 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 
vsp1_drm_init(struct vsp1_device * vsp1)572 int 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 	pipe->output->pipe = pipe;
600 
601 	return 0;
602 }
603 
vsp1_drm_cleanup(struct vsp1_device * vsp1)604 void vsp1_drm_cleanup(struct vsp1_device *vsp1)
605 {
606 }
607