2 * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
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.
14 #include <linux/device.h>
16 #include <media/v4l2-subdev.h>
20 #include "vsp1_pipe.h"
21 #include "vsp1_rwpf.h"
22 #include "vsp1_video.h"
24 #define WPF_GEN2_MAX_WIDTH 2048U
25 #define WPF_GEN2_MAX_HEIGHT 2048U
26 #define WPF_GEN3_MAX_WIDTH 8190U
27 #define WPF_GEN3_MAX_HEIGHT 8190U
29 /* -----------------------------------------------------------------------------
33 static inline void vsp1_wpf_write(struct vsp1_rwpf
*wpf
,
34 struct vsp1_dl_list
*dl
, u32 reg
, u32 data
)
36 vsp1_dl_list_write(dl
, reg
+ wpf
->entity
.index
* VI6_WPF_OFFSET
, data
);
39 /* -----------------------------------------------------------------------------
49 static int vsp1_wpf_s_ctrl(struct v4l2_ctrl
*ctrl
)
51 struct vsp1_rwpf
*wpf
=
52 container_of(ctrl
->handler
, struct vsp1_rwpf
, ctrls
);
59 for (i
= 0; i
< WPF_CTRL_MAX
; ++i
) {
60 if (wpf
->flip
.ctrls
[i
])
61 flip
|= wpf
->flip
.ctrls
[i
]->val
? BIT(i
) : 0;
64 spin_lock_irq(&wpf
->flip
.lock
);
65 wpf
->flip
.pending
= flip
;
66 spin_unlock_irq(&wpf
->flip
.lock
);
76 static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops
= {
77 .s_ctrl
= vsp1_wpf_s_ctrl
,
80 static int wpf_init_controls(struct vsp1_rwpf
*wpf
)
82 struct vsp1_device
*vsp1
= wpf
->entity
.vsp1
;
83 unsigned int num_flip_ctrls
;
85 spin_lock_init(&wpf
->flip
.lock
);
87 if (wpf
->entity
.index
!= 0) {
88 /* Only WPF0 supports flipping. */
90 } else if (vsp1
->info
->features
& VSP1_HAS_WPF_HFLIP
) {
91 /* When horizontal flip is supported the WPF implements two
92 * controls (horizontal flip and vertical flip).
95 } else if (vsp1
->info
->features
& VSP1_HAS_WPF_VFLIP
) {
96 /* When only vertical flip is supported the WPF implements a
97 * single control (vertical flip).
101 /* Otherwise flipping is not supported. */
105 vsp1_rwpf_init_ctrls(wpf
, num_flip_ctrls
);
107 if (num_flip_ctrls
>= 1) {
108 wpf
->flip
.ctrls
[WPF_CTRL_VFLIP
] =
109 v4l2_ctrl_new_std(&wpf
->ctrls
, &vsp1_wpf_ctrl_ops
,
110 V4L2_CID_VFLIP
, 0, 1, 1, 0);
113 if (num_flip_ctrls
== 2) {
114 wpf
->flip
.ctrls
[WPF_CTRL_HFLIP
] =
115 v4l2_ctrl_new_std(&wpf
->ctrls
, &vsp1_wpf_ctrl_ops
,
116 V4L2_CID_HFLIP
, 0, 1, 1, 0);
118 v4l2_ctrl_cluster(2, wpf
->flip
.ctrls
);
121 if (wpf
->ctrls
.error
) {
122 dev_err(vsp1
->dev
, "wpf%u: failed to initialize controls\n",
124 return wpf
->ctrls
.error
;
130 /* -----------------------------------------------------------------------------
131 * V4L2 Subdevice Core Operations
134 static int wpf_s_stream(struct v4l2_subdev
*subdev
, int enable
)
136 struct vsp1_rwpf
*wpf
= to_rwpf(subdev
);
137 struct vsp1_device
*vsp1
= wpf
->entity
.vsp1
;
142 /* Write to registers directly when stopping the stream as there will be
143 * no pipeline run to apply the display list.
145 vsp1_write(vsp1
, VI6_WPF_IRQ_ENB(wpf
->entity
.index
), 0);
146 vsp1_write(vsp1
, wpf
->entity
.index
* VI6_WPF_OFFSET
+
152 /* -----------------------------------------------------------------------------
153 * V4L2 Subdevice Operations
156 static const struct v4l2_subdev_video_ops wpf_video_ops
= {
157 .s_stream
= wpf_s_stream
,
160 static const struct v4l2_subdev_ops wpf_ops
= {
161 .video
= &wpf_video_ops
,
162 .pad
= &vsp1_rwpf_pad_ops
,
165 /* -----------------------------------------------------------------------------
166 * VSP1 Entity Operations
169 static void vsp1_wpf_destroy(struct vsp1_entity
*entity
)
171 struct vsp1_rwpf
*wpf
= entity_to_rwpf(entity
);
173 vsp1_dlm_destroy(wpf
->dlm
);
176 static void wpf_configure(struct vsp1_entity
*entity
,
177 struct vsp1_pipeline
*pipe
,
178 struct vsp1_dl_list
*dl
,
179 enum vsp1_entity_params params
)
181 struct vsp1_rwpf
*wpf
= to_rwpf(&entity
->subdev
);
182 struct vsp1_device
*vsp1
= wpf
->entity
.vsp1
;
183 const struct v4l2_mbus_framefmt
*source_format
;
184 const struct v4l2_mbus_framefmt
*sink_format
;
189 if (params
== VSP1_ENTITY_PARAMS_RUNTIME
) {
190 const unsigned int mask
= BIT(WPF_CTRL_VFLIP
)
191 | BIT(WPF_CTRL_HFLIP
);
194 spin_lock_irqsave(&wpf
->flip
.lock
, flags
);
195 wpf
->flip
.active
= (wpf
->flip
.active
& ~mask
)
196 | (wpf
->flip
.pending
& mask
);
197 spin_unlock_irqrestore(&wpf
->flip
.lock
, flags
);
199 outfmt
= (wpf
->alpha
<< VI6_WPF_OUTFMT_PDV_SHIFT
) | wpf
->outfmt
;
201 if (wpf
->flip
.active
& BIT(WPF_CTRL_VFLIP
))
202 outfmt
|= VI6_WPF_OUTFMT_FLP
;
203 if (wpf
->flip
.active
& BIT(WPF_CTRL_HFLIP
))
204 outfmt
|= VI6_WPF_OUTFMT_HFLP
;
206 vsp1_wpf_write(wpf
, dl
, VI6_WPF_OUTFMT
, outfmt
);
210 sink_format
= vsp1_entity_get_pad_format(&wpf
->entity
,
213 source_format
= vsp1_entity_get_pad_format(&wpf
->entity
,
217 if (params
== VSP1_ENTITY_PARAMS_PARTITION
) {
218 const struct v4l2_pix_format_mplane
*format
= &wpf
->format
;
219 struct vsp1_rwpf_memory mem
= wpf
->mem
;
220 unsigned int flip
= wpf
->flip
.active
;
221 unsigned int width
= source_format
->width
;
222 unsigned int height
= source_format
->height
;
226 * Cropping. The partition algorithm can split the image into
229 if (pipe
->partitions
> 1)
230 width
= pipe
->partition
.width
;
232 vsp1_wpf_write(wpf
, dl
, VI6_WPF_HSZCLIP
, VI6_WPF_SZCLIP_EN
|
233 (0 << VI6_WPF_SZCLIP_OFST_SHIFT
) |
234 (width
<< VI6_WPF_SZCLIP_SIZE_SHIFT
));
235 vsp1_wpf_write(wpf
, dl
, VI6_WPF_VSZCLIP
, VI6_WPF_SZCLIP_EN
|
236 (0 << VI6_WPF_SZCLIP_OFST_SHIFT
) |
237 (height
<< VI6_WPF_SZCLIP_SIZE_SHIFT
));
243 * Update the memory offsets based on flipping configuration.
244 * The destination addresses point to the locations where the
245 * VSP starts writing to memory, which can be different corners
246 * of the image depending on vertical flipping.
248 if (pipe
->partitions
> 1) {
249 const struct vsp1_format_info
*fmtinfo
= wpf
->fmtinfo
;
252 * Horizontal flipping is handled through a line buffer
253 * and doesn't modify the start address, but still needs
254 * to be handled when image partitioning is in effect to
255 * order the partitions correctly.
257 if (flip
& BIT(WPF_CTRL_HFLIP
))
258 offset
= format
->width
- pipe
->partition
.left
259 - pipe
->partition
.width
;
261 offset
= pipe
->partition
.left
;
263 mem
.addr
[0] += offset
* fmtinfo
->bpp
[0] / 8;
264 if (format
->num_planes
> 1) {
265 mem
.addr
[1] += offset
/ fmtinfo
->hsub
266 * fmtinfo
->bpp
[1] / 8;
267 mem
.addr
[2] += offset
/ fmtinfo
->hsub
268 * fmtinfo
->bpp
[2] / 8;
272 if (flip
& BIT(WPF_CTRL_VFLIP
)) {
273 mem
.addr
[0] += (format
->height
- 1)
274 * format
->plane_fmt
[0].bytesperline
;
276 if (format
->num_planes
> 1) {
277 offset
= (format
->height
/ wpf
->fmtinfo
->vsub
- 1)
278 * format
->plane_fmt
[1].bytesperline
;
279 mem
.addr
[1] += offset
;
280 mem
.addr
[2] += offset
;
284 vsp1_wpf_write(wpf
, dl
, VI6_WPF_DSTM_ADDR_Y
, mem
.addr
[0]);
285 vsp1_wpf_write(wpf
, dl
, VI6_WPF_DSTM_ADDR_C0
, mem
.addr
[1]);
286 vsp1_wpf_write(wpf
, dl
, VI6_WPF_DSTM_ADDR_C1
, mem
.addr
[2]);
292 const struct v4l2_pix_format_mplane
*format
= &wpf
->format
;
293 const struct vsp1_format_info
*fmtinfo
= wpf
->fmtinfo
;
295 outfmt
= fmtinfo
->hwfmt
<< VI6_WPF_OUTFMT_WRFMT_SHIFT
;
298 outfmt
|= VI6_WPF_OUTFMT_PXA
;
299 if (fmtinfo
->swap_yc
)
300 outfmt
|= VI6_WPF_OUTFMT_SPYCS
;
301 if (fmtinfo
->swap_uv
)
302 outfmt
|= VI6_WPF_OUTFMT_SPUVS
;
304 /* Destination stride and byte swapping. */
305 vsp1_wpf_write(wpf
, dl
, VI6_WPF_DSTM_STRIDE_Y
,
306 format
->plane_fmt
[0].bytesperline
);
307 if (format
->num_planes
> 1)
308 vsp1_wpf_write(wpf
, dl
, VI6_WPF_DSTM_STRIDE_C
,
309 format
->plane_fmt
[1].bytesperline
);
311 vsp1_wpf_write(wpf
, dl
, VI6_WPF_DSWAP
, fmtinfo
->swap
);
313 if (vsp1
->info
->features
& VSP1_HAS_WPF_HFLIP
&&
314 wpf
->entity
.index
== 0)
315 vsp1_wpf_write(wpf
, dl
, VI6_WPF_ROT_CTRL
,
316 VI6_WPF_ROT_CTRL_LN16
|
317 (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT
));
320 if (sink_format
->code
!= source_format
->code
)
321 outfmt
|= VI6_WPF_OUTFMT_CSC
;
323 wpf
->outfmt
= outfmt
;
325 vsp1_dl_list_write(dl
, VI6_DPR_WPF_FPORCH(wpf
->entity
.index
),
326 VI6_DPR_WPF_FPORCH_FP_WPFN
);
328 vsp1_dl_list_write(dl
, VI6_WPF_WRBCK_CTRL
, 0);
330 /* Sources. If the pipeline has a single input and BRU is not used,
331 * configure it as the master layer. Otherwise configure all
332 * inputs as sub-layers and select the virtual RPF as the master
335 for (i
= 0; i
< vsp1
->info
->rpf_count
; ++i
) {
336 struct vsp1_rwpf
*input
= pipe
->inputs
[i
];
341 srcrpf
|= (!pipe
->bru
&& pipe
->num_inputs
== 1)
342 ? VI6_WPF_SRCRPF_RPF_ACT_MST(input
->entity
.index
)
343 : VI6_WPF_SRCRPF_RPF_ACT_SUB(input
->entity
.index
);
346 if (pipe
->bru
|| pipe
->num_inputs
> 1)
347 srcrpf
|= VI6_WPF_SRCRPF_VIRACT_MST
;
349 vsp1_wpf_write(wpf
, dl
, VI6_WPF_SRCRPF
, srcrpf
);
351 /* Enable interrupts */
352 vsp1_dl_list_write(dl
, VI6_WPF_IRQ_STA(wpf
->entity
.index
), 0);
353 vsp1_dl_list_write(dl
, VI6_WPF_IRQ_ENB(wpf
->entity
.index
),
354 VI6_WFP_IRQ_ENB_DFEE
);
357 static const struct vsp1_entity_operations wpf_entity_ops
= {
358 .destroy
= vsp1_wpf_destroy
,
359 .configure
= wpf_configure
,
362 /* -----------------------------------------------------------------------------
363 * Initialization and Cleanup
366 struct vsp1_rwpf
*vsp1_wpf_create(struct vsp1_device
*vsp1
, unsigned int index
)
368 struct vsp1_rwpf
*wpf
;
372 wpf
= devm_kzalloc(vsp1
->dev
, sizeof(*wpf
), GFP_KERNEL
);
374 return ERR_PTR(-ENOMEM
);
376 if (vsp1
->info
->gen
== 2) {
377 wpf
->max_width
= WPF_GEN2_MAX_WIDTH
;
378 wpf
->max_height
= WPF_GEN2_MAX_HEIGHT
;
380 wpf
->max_width
= WPF_GEN3_MAX_WIDTH
;
381 wpf
->max_height
= WPF_GEN3_MAX_HEIGHT
;
384 wpf
->entity
.ops
= &wpf_entity_ops
;
385 wpf
->entity
.type
= VSP1_ENTITY_WPF
;
386 wpf
->entity
.index
= index
;
388 sprintf(name
, "wpf.%u", index
);
389 ret
= vsp1_entity_init(vsp1
, &wpf
->entity
, name
, 2, &wpf_ops
,
390 MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER
);
394 /* Initialize the display list manager. */
395 wpf
->dlm
= vsp1_dlm_create(vsp1
, index
, 64);
401 /* Initialize the control handler. */
402 ret
= wpf_init_controls(wpf
);
404 dev_err(vsp1
->dev
, "wpf%u: failed to initialize controls\n",
409 v4l2_ctrl_handler_setup(&wpf
->ctrls
);
414 vsp1_entity_destroy(&wpf
->entity
);