diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2016-06-20 05:07:08 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2017-04-14 21:36:03 -0400 |
commit | 3e9a0e0bfafdf6c28c520d43fd64c5775d04662f (patch) | |
tree | 80f6bed9a8978beefe863a4e3a0d0ad6bc33ee8a | |
parent | 99bb078eee469f7284b1dca36cf9edf7af1a92bf (diff) |
[media] v4l: vsp1: wpf: Implement rotation support
Some WPF instances, on Gen3 devices, can perform 90° rotation when
writing frames to memory. Implement support for this using the
V4L2_CID_ROTATE control.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_rpf.c | 2 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_rwpf.c | 5 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_rwpf.h | 7 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.c | 12 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_wpf.c | 205 |
5 files changed, 177 insertions, 54 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index f5a9a4c8c74d..8feddd59cf8d 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c | |||
@@ -106,7 +106,7 @@ static void rpf_configure(struct vsp1_entity *entity, | |||
106 | * of the pipeline. | 106 | * of the pipeline. |
107 | */ | 107 | */ |
108 | output = vsp1_entity_get_pad_format(wpf, wpf->config, | 108 | output = vsp1_entity_get_pad_format(wpf, wpf->config, |
109 | RWPF_PAD_SOURCE); | 109 | RWPF_PAD_SINK); |
110 | 110 | ||
111 | crop.width = pipe->partition.width * input_width | 111 | crop.width = pipe->partition.width * input_width |
112 | / output->width; | 112 | / output->width; |
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 7d52c88a583e..cfd8f1904fa6 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c | |||
@@ -121,6 +121,11 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, | |||
121 | RWPF_PAD_SOURCE); | 121 | RWPF_PAD_SOURCE); |
122 | *format = fmt->format; | 122 | *format = fmt->format; |
123 | 123 | ||
124 | if (rwpf->flip.rotate) { | ||
125 | format->width = fmt->format.height; | ||
126 | format->height = fmt->format.width; | ||
127 | } | ||
128 | |||
124 | done: | 129 | done: |
125 | mutex_unlock(&rwpf->entity.lock); | 130 | mutex_unlock(&rwpf->entity.lock); |
126 | return ret; | 131 | return ret; |
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 1c98aff3da5d..58215a7ab631 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h | |||
@@ -56,9 +56,14 @@ struct vsp1_rwpf { | |||
56 | 56 | ||
57 | struct { | 57 | struct { |
58 | spinlock_t lock; | 58 | spinlock_t lock; |
59 | struct v4l2_ctrl *ctrls[2]; | 59 | struct { |
60 | struct v4l2_ctrl *vflip; | ||
61 | struct v4l2_ctrl *hflip; | ||
62 | struct v4l2_ctrl *rotate; | ||
63 | } ctrls; | ||
60 | unsigned int pending; | 64 | unsigned int pending; |
61 | unsigned int active; | 65 | unsigned int active; |
66 | bool rotate; | ||
62 | } flip; | 67 | } flip; |
63 | 68 | ||
64 | struct vsp1_rwpf_memory mem; | 69 | struct vsp1_rwpf_memory mem; |
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 5239e08fabc3..795a3ca9ca03 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c | |||
@@ -187,9 +187,13 @@ static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) | |||
187 | struct vsp1_entity *entity; | 187 | struct vsp1_entity *entity; |
188 | unsigned int div_size; | 188 | unsigned int div_size; |
189 | 189 | ||
190 | /* | ||
191 | * Partitions are computed on the size before rotation, use the format | ||
192 | * at the WPF sink. | ||
193 | */ | ||
190 | format = vsp1_entity_get_pad_format(&pipe->output->entity, | 194 | format = vsp1_entity_get_pad_format(&pipe->output->entity, |
191 | pipe->output->entity.config, | 195 | pipe->output->entity.config, |
192 | RWPF_PAD_SOURCE); | 196 | RWPF_PAD_SINK); |
193 | div_size = format->width; | 197 | div_size = format->width; |
194 | 198 | ||
195 | /* Gen2 hardware doesn't require image partitioning. */ | 199 | /* Gen2 hardware doesn't require image partitioning. */ |
@@ -229,9 +233,13 @@ static struct v4l2_rect vsp1_video_partition(struct vsp1_pipeline *pipe, | |||
229 | struct v4l2_rect partition; | 233 | struct v4l2_rect partition; |
230 | unsigned int modulus; | 234 | unsigned int modulus; |
231 | 235 | ||
236 | /* | ||
237 | * Partitions are computed on the size before rotation, use the format | ||
238 | * at the WPF sink. | ||
239 | */ | ||
232 | format = vsp1_entity_get_pad_format(&pipe->output->entity, | 240 | format = vsp1_entity_get_pad_format(&pipe->output->entity, |
233 | pipe->output->entity.config, | 241 | pipe->output->entity.config, |
234 | RWPF_PAD_SOURCE); | 242 | RWPF_PAD_SINK); |
235 | 243 | ||
236 | /* A single partition simply processes the output size in full. */ | 244 | /* A single partition simply processes the output size in full. */ |
237 | if (pipe->partitions <= 1) { | 245 | if (pipe->partitions <= 1) { |
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 25a2ed6e2e18..32df109b119f 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c | |||
@@ -43,32 +43,90 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, | |||
43 | enum wpf_flip_ctrl { | 43 | enum wpf_flip_ctrl { |
44 | WPF_CTRL_VFLIP = 0, | 44 | WPF_CTRL_VFLIP = 0, |
45 | WPF_CTRL_HFLIP = 1, | 45 | WPF_CTRL_HFLIP = 1, |
46 | WPF_CTRL_MAX, | ||
47 | }; | 46 | }; |
48 | 47 | ||
48 | static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) | ||
49 | { | ||
50 | struct vsp1_video *video = wpf->video; | ||
51 | struct v4l2_mbus_framefmt *sink_format; | ||
52 | struct v4l2_mbus_framefmt *source_format; | ||
53 | bool rotate; | ||
54 | int ret = 0; | ||
55 | |||
56 | /* | ||
57 | * Only consider the 0°/180° from/to 90°/270° modifications, the rest | ||
58 | * is taken care of by the flipping configuration. | ||
59 | */ | ||
60 | rotate = rotation == 90 || rotation == 270; | ||
61 | if (rotate == wpf->flip.rotate) | ||
62 | return 0; | ||
63 | |||
64 | /* Changing rotation isn't allowed when buffers are allocated. */ | ||
65 | mutex_lock(&video->lock); | ||
66 | |||
67 | if (vb2_is_busy(&video->queue)) { | ||
68 | ret = -EBUSY; | ||
69 | goto done; | ||
70 | } | ||
71 | |||
72 | sink_format = vsp1_entity_get_pad_format(&wpf->entity, | ||
73 | wpf->entity.config, | ||
74 | RWPF_PAD_SINK); | ||
75 | source_format = vsp1_entity_get_pad_format(&wpf->entity, | ||
76 | wpf->entity.config, | ||
77 | RWPF_PAD_SOURCE); | ||
78 | |||
79 | mutex_lock(&wpf->entity.lock); | ||
80 | |||
81 | if (rotate) { | ||
82 | source_format->width = sink_format->height; | ||
83 | source_format->height = sink_format->width; | ||
84 | } else { | ||
85 | source_format->width = sink_format->width; | ||
86 | source_format->height = sink_format->height; | ||
87 | } | ||
88 | |||
89 | wpf->flip.rotate = rotate; | ||
90 | |||
91 | mutex_unlock(&wpf->entity.lock); | ||
92 | |||
93 | done: | ||
94 | mutex_unlock(&video->lock); | ||
95 | return ret; | ||
96 | } | ||
97 | |||
49 | static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) | 98 | static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) |
50 | { | 99 | { |
51 | struct vsp1_rwpf *wpf = | 100 | struct vsp1_rwpf *wpf = |
52 | container_of(ctrl->handler, struct vsp1_rwpf, ctrls); | 101 | container_of(ctrl->handler, struct vsp1_rwpf, ctrls); |
53 | unsigned int i; | 102 | unsigned int rotation; |
54 | u32 flip = 0; | 103 | u32 flip = 0; |
104 | int ret; | ||
55 | 105 | ||
56 | switch (ctrl->id) { | 106 | /* Update the rotation. */ |
57 | case V4L2_CID_HFLIP: | 107 | rotation = wpf->flip.ctrls.rotate ? wpf->flip.ctrls.rotate->val : 0; |
58 | case V4L2_CID_VFLIP: | 108 | ret = vsp1_wpf_set_rotation(wpf, rotation); |
59 | for (i = 0; i < WPF_CTRL_MAX; ++i) { | 109 | if (ret < 0) |
60 | if (wpf->flip.ctrls[i]) | 110 | return ret; |
61 | flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0; | ||
62 | } | ||
63 | 111 | ||
64 | spin_lock_irq(&wpf->flip.lock); | 112 | /* |
65 | wpf->flip.pending = flip; | 113 | * Compute the flip value resulting from all three controls, with |
66 | spin_unlock_irq(&wpf->flip.lock); | 114 | * rotation by 180° flipping the image in both directions. Store the |
67 | break; | 115 | * result in the pending flip field for the next frame that will be |
116 | * processed. | ||
117 | */ | ||
118 | if (wpf->flip.ctrls.vflip->val) | ||
119 | flip |= BIT(WPF_CTRL_VFLIP); | ||
68 | 120 | ||
69 | default: | 121 | if (wpf->flip.ctrls.hflip && wpf->flip.ctrls.hflip->val) |
70 | return -EINVAL; | 122 | flip |= BIT(WPF_CTRL_HFLIP); |
71 | } | 123 | |
124 | if (rotation == 180 || rotation == 270) | ||
125 | flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); | ||
126 | |||
127 | spin_lock_irq(&wpf->flip.lock); | ||
128 | wpf->flip.pending = flip; | ||
129 | spin_unlock_irq(&wpf->flip.lock); | ||
72 | 130 | ||
73 | return 0; | 131 | return 0; |
74 | } | 132 | } |
@@ -89,10 +147,10 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) | |||
89 | num_flip_ctrls = 0; | 147 | num_flip_ctrls = 0; |
90 | } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { | 148 | } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { |
91 | /* | 149 | /* |
92 | * When horizontal flip is supported the WPF implements two | 150 | * When horizontal flip is supported the WPF implements three |
93 | * controls (horizontal flip and vertical flip). | 151 | * controls (horizontal flip, vertical flip and rotation). |
94 | */ | 152 | */ |
95 | num_flip_ctrls = 2; | 153 | num_flip_ctrls = 3; |
96 | } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { | 154 | } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { |
97 | /* | 155 | /* |
98 | * When only vertical flip is supported the WPF implements a | 156 | * When only vertical flip is supported the WPF implements a |
@@ -107,17 +165,19 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) | |||
107 | vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); | 165 | vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); |
108 | 166 | ||
109 | if (num_flip_ctrls >= 1) { | 167 | if (num_flip_ctrls >= 1) { |
110 | wpf->flip.ctrls[WPF_CTRL_VFLIP] = | 168 | wpf->flip.ctrls.vflip = |
111 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, | 169 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, |
112 | V4L2_CID_VFLIP, 0, 1, 1, 0); | 170 | V4L2_CID_VFLIP, 0, 1, 1, 0); |
113 | } | 171 | } |
114 | 172 | ||
115 | if (num_flip_ctrls == 2) { | 173 | if (num_flip_ctrls == 3) { |
116 | wpf->flip.ctrls[WPF_CTRL_HFLIP] = | 174 | wpf->flip.ctrls.hflip = |
117 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, | 175 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, |
118 | V4L2_CID_HFLIP, 0, 1, 1, 0); | 176 | V4L2_CID_HFLIP, 0, 1, 1, 0); |
119 | 177 | wpf->flip.ctrls.rotate = | |
120 | v4l2_ctrl_cluster(2, wpf->flip.ctrls); | 178 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, |
179 | V4L2_CID_ROTATE, 0, 270, 90, 0); | ||
180 | v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip); | ||
121 | } | 181 | } |
122 | 182 | ||
123 | if (wpf->ctrls.error) { | 183 | if (wpf->ctrls.error) { |
@@ -222,8 +282,8 @@ static void wpf_configure(struct vsp1_entity *entity, | |||
222 | const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; | 282 | const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; |
223 | struct vsp1_rwpf_memory mem = wpf->mem; | 283 | struct vsp1_rwpf_memory mem = wpf->mem; |
224 | unsigned int flip = wpf->flip.active; | 284 | unsigned int flip = wpf->flip.active; |
225 | unsigned int width = source_format->width; | 285 | unsigned int width = sink_format->width; |
226 | unsigned int height = source_format->height; | 286 | unsigned int height = sink_format->height; |
227 | unsigned int offset; | 287 | unsigned int offset; |
228 | 288 | ||
229 | /* | 289 | /* |
@@ -246,45 +306,78 @@ static void wpf_configure(struct vsp1_entity *entity, | |||
246 | /* | 306 | /* |
247 | * Update the memory offsets based on flipping configuration. | 307 | * Update the memory offsets based on flipping configuration. |
248 | * The destination addresses point to the locations where the | 308 | * The destination addresses point to the locations where the |
249 | * VSP starts writing to memory, which can be different corners | 309 | * VSP starts writing to memory, which can be any corner of the |
250 | * of the image depending on vertical flipping. | 310 | * image depending on the combination of flipping and rotation. |
251 | */ | 311 | */ |
252 | if (pipe->partitions > 1) { | ||
253 | const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; | ||
254 | 312 | ||
255 | /* | 313 | /* |
256 | * Horizontal flipping is handled through a line buffer | 314 | * First take the partition left coordinate into account. |
257 | * and doesn't modify the start address, but still needs | 315 | * Compute the offset to order the partitions correctly on the |
258 | * to be handled when image partitioning is in effect to | 316 | * output based on whether flipping is enabled. Consider |
259 | * order the partitions correctly. | 317 | * horizontal flipping when rotation is disabled but vertical |
260 | */ | 318 | * flipping when rotation is enabled, as rotating the image |
261 | if (flip & BIT(WPF_CTRL_HFLIP)) | 319 | * switches the horizontal and vertical directions. The offset |
262 | offset = format->width - pipe->partition.left | 320 | * is applied horizontally or vertically accordingly. |
263 | - pipe->partition.width; | 321 | */ |
322 | if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) | ||
323 | offset = format->width - pipe->partition.left | ||
324 | - pipe->partition.width; | ||
325 | else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) | ||
326 | offset = format->height - pipe->partition.left | ||
327 | - pipe->partition.width; | ||
328 | else | ||
329 | offset = pipe->partition.left; | ||
330 | |||
331 | for (i = 0; i < format->num_planes; ++i) { | ||
332 | unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; | ||
333 | unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; | ||
334 | |||
335 | if (wpf->flip.rotate) | ||
336 | mem.addr[i] += offset / vsub | ||
337 | * format->plane_fmt[i].bytesperline; | ||
264 | else | 338 | else |
265 | offset = pipe->partition.left; | 339 | mem.addr[i] += offset / hsub |
266 | 340 | * fmtinfo->bpp[i] / 8; | |
267 | mem.addr[0] += offset * fmtinfo->bpp[0] / 8; | ||
268 | if (format->num_planes > 1) { | ||
269 | mem.addr[1] += offset / fmtinfo->hsub | ||
270 | * fmtinfo->bpp[1] / 8; | ||
271 | mem.addr[2] += offset / fmtinfo->hsub | ||
272 | * fmtinfo->bpp[2] / 8; | ||
273 | } | ||
274 | } | 341 | } |
275 | 342 | ||
276 | if (flip & BIT(WPF_CTRL_VFLIP)) { | 343 | if (flip & BIT(WPF_CTRL_VFLIP)) { |
277 | mem.addr[0] += (format->height - 1) | 344 | /* |
345 | * When rotating the output (after rotation) image | ||
346 | * height is equal to the partition width (before | ||
347 | * rotation). Otherwise it is equal to the output | ||
348 | * image height. | ||
349 | */ | ||
350 | if (wpf->flip.rotate) | ||
351 | height = pipe->partition.width; | ||
352 | else | ||
353 | height = format->height; | ||
354 | |||
355 | mem.addr[0] += (height - 1) | ||
278 | * format->plane_fmt[0].bytesperline; | 356 | * format->plane_fmt[0].bytesperline; |
279 | 357 | ||
280 | if (format->num_planes > 1) { | 358 | if (format->num_planes > 1) { |
281 | offset = (format->height / wpf->fmtinfo->vsub - 1) | 359 | offset = (height / fmtinfo->vsub - 1) |
282 | * format->plane_fmt[1].bytesperline; | 360 | * format->plane_fmt[1].bytesperline; |
283 | mem.addr[1] += offset; | 361 | mem.addr[1] += offset; |
284 | mem.addr[2] += offset; | 362 | mem.addr[2] += offset; |
285 | } | 363 | } |
286 | } | 364 | } |
287 | 365 | ||
366 | if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) { | ||
367 | unsigned int hoffset = max(0, (int)format->width - 16); | ||
368 | |||
369 | /* | ||
370 | * Compute the output coordinate. The partition | ||
371 | * horizontal (left) offset becomes a vertical offset. | ||
372 | */ | ||
373 | for (i = 0; i < format->num_planes; ++i) { | ||
374 | unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; | ||
375 | |||
376 | mem.addr[i] += hoffset / hsub | ||
377 | * fmtinfo->bpp[i] / 8; | ||
378 | } | ||
379 | } | ||
380 | |||
288 | /* | 381 | /* |
289 | * On Gen3 hardware the SPUVS bit has no effect on 3-planar | 382 | * On Gen3 hardware the SPUVS bit has no effect on 3-planar |
290 | * formats. Swap the U and V planes manually in that case. | 383 | * formats. Swap the U and V planes manually in that case. |
@@ -306,6 +399,9 @@ static void wpf_configure(struct vsp1_entity *entity, | |||
306 | 399 | ||
307 | outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; | 400 | outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; |
308 | 401 | ||
402 | if (wpf->flip.rotate) | ||
403 | outfmt |= VI6_WPF_OUTFMT_ROT; | ||
404 | |||
309 | if (fmtinfo->alpha) | 405 | if (fmtinfo->alpha) |
310 | outfmt |= VI6_WPF_OUTFMT_PXA; | 406 | outfmt |= VI6_WPF_OUTFMT_PXA; |
311 | if (fmtinfo->swap_yc) | 407 | if (fmtinfo->swap_yc) |
@@ -367,9 +463,18 @@ static void wpf_configure(struct vsp1_entity *entity, | |||
367 | VI6_WFP_IRQ_ENB_DFEE); | 463 | VI6_WFP_IRQ_ENB_DFEE); |
368 | } | 464 | } |
369 | 465 | ||
466 | static unsigned int wpf_max_width(struct vsp1_entity *entity, | ||
467 | struct vsp1_pipeline *pipe) | ||
468 | { | ||
469 | struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); | ||
470 | |||
471 | return wpf->flip.rotate ? 256 : wpf->max_width; | ||
472 | } | ||
473 | |||
370 | static const struct vsp1_entity_operations wpf_entity_ops = { | 474 | static const struct vsp1_entity_operations wpf_entity_ops = { |
371 | .destroy = vsp1_wpf_destroy, | 475 | .destroy = vsp1_wpf_destroy, |
372 | .configure = wpf_configure, | 476 | .configure = wpf_configure, |
477 | .max_width = wpf_max_width, | ||
373 | }; | 478 | }; |
374 | 479 | ||
375 | /* ----------------------------------------------------------------------------- | 480 | /* ----------------------------------------------------------------------------- |