1
2
3
4
5
6
7
8
9
10
11
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
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
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
87
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
112
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
181
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
190
191
192
193
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
215
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
225 pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
226}
227EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
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
288
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
325
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
364
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
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
421
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
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
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
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
481 list_for_each_entry(entity, &pipe->entities, list_pipe) {
482
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
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
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
533
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
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