diff options
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_pipe.h | 3 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_rpf.c | 46 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_uds.c | 15 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.c | 121 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_wpf.c | 30 |
5 files changed, 195 insertions, 20 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index af4cd23d399b..f15b697ad999 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h | |||
@@ -79,6 +79,7 @@ enum vsp1_pipeline_state { | |||
79 | * @dl: display list associated with the pipeline | 79 | * @dl: display list associated with the pipeline |
80 | * @div_size: The maximum allowed partition size for the pipeline | 80 | * @div_size: The maximum allowed partition size for the pipeline |
81 | * @partitions: The number of partitions used to process one frame | 81 | * @partitions: The number of partitions used to process one frame |
82 | * @current_partition: The partition number currently being configured | ||
82 | */ | 83 | */ |
83 | struct vsp1_pipeline { | 84 | struct vsp1_pipeline { |
84 | struct media_pipeline pipe; | 85 | struct media_pipeline pipe; |
@@ -109,6 +110,8 @@ struct vsp1_pipeline { | |||
109 | 110 | ||
110 | unsigned int div_size; | 111 | unsigned int div_size; |
111 | unsigned int partitions; | 112 | unsigned int partitions; |
113 | struct v4l2_rect partition; | ||
114 | unsigned int current_partition; | ||
112 | }; | 115 | }; |
113 | 116 | ||
114 | void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); | 117 | void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); |
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index de5ef76c5004..e6236ff2f74a 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c | |||
@@ -72,8 +72,8 @@ static void rpf_configure(struct vsp1_entity *entity, | |||
72 | } | 72 | } |
73 | 73 | ||
74 | if (params == VSP1_ENTITY_PARAMS_PARTITION) { | 74 | if (params == VSP1_ENTITY_PARAMS_PARTITION) { |
75 | const struct v4l2_rect *crop; | ||
76 | unsigned int offsets[2]; | 75 | unsigned int offsets[2]; |
76 | struct v4l2_rect crop; | ||
77 | 77 | ||
78 | /* Source size and crop offsets. | 78 | /* Source size and crop offsets. |
79 | * | 79 | * |
@@ -82,21 +82,47 @@ static void rpf_configure(struct vsp1_entity *entity, | |||
82 | * offsets are needed, as planes 2 and 3 always have identical | 82 | * offsets are needed, as planes 2 and 3 always have identical |
83 | * strides. | 83 | * strides. |
84 | */ | 84 | */ |
85 | crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); | 85 | crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); |
86 | |||
87 | /* Partition Algorithm Control | ||
88 | * | ||
89 | * The partition algorithm can split this frame into multiple | ||
90 | * slices. We must scale our partition window based on the pipe | ||
91 | * configuration to match the destination partition window. | ||
92 | * To achieve this, we adjust our crop to provide a 'sub-crop' | ||
93 | * matching the expected partition window. Only 'left' and | ||
94 | * 'width' need to be adjusted. | ||
95 | */ | ||
96 | if (pipe->partitions > 1) { | ||
97 | const struct v4l2_mbus_framefmt *output; | ||
98 | struct vsp1_entity *wpf = &pipe->output->entity; | ||
99 | unsigned int input_width = crop.width; | ||
100 | |||
101 | /* Scale the partition window based on the configuration | ||
102 | * of the pipeline. | ||
103 | */ | ||
104 | output = vsp1_entity_get_pad_format(wpf, wpf->config, | ||
105 | RWPF_PAD_SOURCE); | ||
106 | |||
107 | crop.width = pipe->partition.width * input_width | ||
108 | / output->width; | ||
109 | crop.left += pipe->partition.left * input_width | ||
110 | / output->width; | ||
111 | } | ||
86 | 112 | ||
87 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, | 113 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, |
88 | (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | | 114 | (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | |
89 | (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); | 115 | (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); |
90 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, | 116 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, |
91 | (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | | 117 | (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | |
92 | (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); | 118 | (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); |
93 | 119 | ||
94 | offsets[0] = crop->top * format->plane_fmt[0].bytesperline | 120 | offsets[0] = crop.top * format->plane_fmt[0].bytesperline |
95 | + crop->left * fmtinfo->bpp[0] / 8; | 121 | + crop.left * fmtinfo->bpp[0] / 8; |
96 | 122 | ||
97 | if (format->num_planes > 1) | 123 | if (format->num_planes > 1) |
98 | offsets[1] = crop->top * format->plane_fmt[1].bytesperline | 124 | offsets[1] = crop.top * format->plane_fmt[1].bytesperline |
99 | + crop->left / fmtinfo->hsub | 125 | + crop.left / fmtinfo->hsub |
100 | * fmtinfo->bpp[1] / 8; | 126 | * fmtinfo->bpp[1] / 8; |
101 | else | 127 | else |
102 | offsets[1] = 0; | 128 | offsets[1] = 0; |
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 706b6e85f47d..da8f89a31ea4 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c | |||
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include "vsp1.h" | 19 | #include "vsp1.h" |
20 | #include "vsp1_dl.h" | 20 | #include "vsp1_dl.h" |
21 | #include "vsp1_pipe.h" | ||
21 | #include "vsp1_uds.h" | 22 | #include "vsp1_uds.h" |
22 | 23 | ||
23 | #define UDS_MIN_SIZE 4U | 24 | #define UDS_MIN_SIZE 4U |
@@ -270,6 +271,15 @@ static void uds_configure(struct vsp1_entity *entity, | |||
270 | unsigned int vscale; | 271 | unsigned int vscale; |
271 | bool multitap; | 272 | bool multitap; |
272 | 273 | ||
274 | if (params == VSP1_ENTITY_PARAMS_PARTITION) { | ||
275 | const struct v4l2_rect *clip = &pipe->partition; | ||
276 | |||
277 | vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, | ||
278 | (clip->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | | ||
279 | (clip->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); | ||
280 | return; | ||
281 | } | ||
282 | |||
273 | if (params != VSP1_ENTITY_PARAMS_INIT) | 283 | if (params != VSP1_ENTITY_PARAMS_INIT) |
274 | return; | 284 | return; |
275 | 285 | ||
@@ -302,13 +312,10 @@ static void uds_configure(struct vsp1_entity *entity, | |||
302 | (uds_passband_width(vscale) | 312 | (uds_passband_width(vscale) |
303 | << VI6_UDS_PASS_BWIDTH_V_SHIFT)); | 313 | << VI6_UDS_PASS_BWIDTH_V_SHIFT)); |
304 | 314 | ||
305 | /* Set the scaling ratios and the output size. */ | 315 | /* Set the scaling ratios. */ |
306 | vsp1_uds_write(uds, dl, VI6_UDS_SCALE, | 316 | vsp1_uds_write(uds, dl, VI6_UDS_SCALE, |
307 | (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | | 317 | (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | |
308 | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); | 318 | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); |
309 | vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, | ||
310 | (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | | ||
311 | (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); | ||
312 | } | 319 | } |
313 | 320 | ||
314 | static unsigned int uds_max_width(struct vsp1_entity *entity, | 321 | static unsigned int uds_max_width(struct vsp1_entity *entity, |
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index b903cc5471e0..15d08cb50bd1 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c | |||
@@ -205,6 +205,74 @@ static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) | |||
205 | pipe->partitions = DIV_ROUND_UP(format->width, div_size); | 205 | pipe->partitions = DIV_ROUND_UP(format->width, div_size); |
206 | } | 206 | } |
207 | 207 | ||
208 | /* | ||
209 | * vsp1_video_partition - Calculate the active partition output window | ||
210 | * | ||
211 | * @div_size: pre-determined maximum partition division size | ||
212 | * @index: partition index | ||
213 | * | ||
214 | * Returns a v4l2_rect describing the partition window. | ||
215 | */ | ||
216 | static struct v4l2_rect vsp1_video_partition(struct vsp1_pipeline *pipe, | ||
217 | unsigned int div_size, | ||
218 | unsigned int index) | ||
219 | { | ||
220 | const struct v4l2_mbus_framefmt *format; | ||
221 | struct v4l2_rect partition; | ||
222 | unsigned int modulus; | ||
223 | |||
224 | format = vsp1_entity_get_pad_format(&pipe->output->entity, | ||
225 | pipe->output->entity.config, | ||
226 | RWPF_PAD_SOURCE); | ||
227 | |||
228 | /* A single partition simply processes the output size in full. */ | ||
229 | if (pipe->partitions <= 1) { | ||
230 | partition.left = 0; | ||
231 | partition.top = 0; | ||
232 | partition.width = format->width; | ||
233 | partition.height = format->height; | ||
234 | return partition; | ||
235 | } | ||
236 | |||
237 | /* Initialise the partition with sane starting conditions. */ | ||
238 | partition.left = index * div_size; | ||
239 | partition.top = 0; | ||
240 | partition.width = div_size; | ||
241 | partition.height = format->height; | ||
242 | |||
243 | modulus = format->width % div_size; | ||
244 | |||
245 | /* We need to prevent the last partition from being smaller than the | ||
246 | * *minimum* width of the hardware capabilities. | ||
247 | * | ||
248 | * If the modulus is less than half of the partition size, | ||
249 | * the penultimate partition is reduced to half, which is added | ||
250 | * to the final partition: |1234|1234|1234|12|341| | ||
251 | * to prevents this: |1234|1234|1234|1234|1|. | ||
252 | */ | ||
253 | if (modulus) { | ||
254 | /* pipe->partitions is 1 based, whilst index is a 0 based index. | ||
255 | * Normalise this locally. | ||
256 | */ | ||
257 | unsigned int partitions = pipe->partitions - 1; | ||
258 | |||
259 | if (modulus < div_size / 2) { | ||
260 | if (index == partitions - 1) { | ||
261 | /* Halve the penultimate partition. */ | ||
262 | partition.width = div_size / 2; | ||
263 | } else if (index == partitions) { | ||
264 | /* Increase the final partition. */ | ||
265 | partition.width = (div_size / 2) + modulus; | ||
266 | partition.left -= div_size / 2; | ||
267 | } | ||
268 | } else if (index == partitions) { | ||
269 | partition.width = modulus; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | return partition; | ||
274 | } | ||
275 | |||
208 | /* ----------------------------------------------------------------------------- | 276 | /* ----------------------------------------------------------------------------- |
209 | * Pipeline Management | 277 | * Pipeline Management |
210 | */ | 278 | */ |
@@ -280,22 +348,69 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, | |||
280 | pipe->buffers_ready |= 1 << video->pipe_index; | 348 | pipe->buffers_ready |= 1 << video->pipe_index; |
281 | } | 349 | } |
282 | 350 | ||
351 | static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, | ||
352 | struct vsp1_dl_list *dl) | ||
353 | { | ||
354 | struct vsp1_entity *entity; | ||
355 | |||
356 | pipe->partition = vsp1_video_partition(pipe, pipe->div_size, | ||
357 | pipe->current_partition); | ||
358 | |||
359 | list_for_each_entry(entity, &pipe->entities, list_pipe) { | ||
360 | if (entity->ops->configure) | ||
361 | entity->ops->configure(entity, pipe, dl, | ||
362 | VSP1_ENTITY_PARAMS_PARTITION); | ||
363 | } | ||
364 | } | ||
365 | |||
283 | static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) | 366 | static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) |
284 | { | 367 | { |
368 | struct vsp1_device *vsp1 = pipe->output->entity.vsp1; | ||
285 | struct vsp1_entity *entity; | 369 | struct vsp1_entity *entity; |
286 | 370 | ||
287 | if (!pipe->dl) | 371 | if (!pipe->dl) |
288 | pipe->dl = vsp1_dl_list_get(pipe->output->dlm); | 372 | pipe->dl = vsp1_dl_list_get(pipe->output->dlm); |
289 | 373 | ||
374 | /* Start with the runtime parameters as the configure operation can | ||
375 | * compute/cache information needed when configuring partitions. This | ||
376 | * is the case with flipping in the WPF. | ||
377 | */ | ||
290 | list_for_each_entry(entity, &pipe->entities, list_pipe) { | 378 | list_for_each_entry(entity, &pipe->entities, list_pipe) { |
291 | if (entity->ops->configure) { | 379 | if (entity->ops->configure) |
292 | entity->ops->configure(entity, pipe, pipe->dl, | 380 | entity->ops->configure(entity, pipe, pipe->dl, |
293 | VSP1_ENTITY_PARAMS_RUNTIME); | 381 | VSP1_ENTITY_PARAMS_RUNTIME); |
294 | entity->ops->configure(entity, pipe, pipe->dl, | 382 | } |
295 | VSP1_ENTITY_PARAMS_PARTITION); | 383 | |
384 | /* Run the first partition */ | ||
385 | pipe->current_partition = 0; | ||
386 | vsp1_video_pipeline_run_partition(pipe, pipe->dl); | ||
387 | |||
388 | /* Process consecutive partitions as necessary */ | ||
389 | for (pipe->current_partition = 1; | ||
390 | pipe->current_partition < pipe->partitions; | ||
391 | pipe->current_partition++) { | ||
392 | struct vsp1_dl_list *dl; | ||
393 | |||
394 | /* Partition configuration operations will utilise | ||
395 | * the pipe->current_partition variable to determine | ||
396 | * the work they should complete. | ||
397 | */ | ||
398 | dl = vsp1_dl_list_get(pipe->output->dlm); | ||
399 | |||
400 | /* An incomplete chain will still function, but output only | ||
401 | * the partitions that had a dl available. The frame end | ||
402 | * interrupt will be marked on the last dl in the chain. | ||
403 | */ | ||
404 | if (!dl) { | ||
405 | dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n"); | ||
406 | break; | ||
296 | } | 407 | } |
408 | |||
409 | vsp1_video_pipeline_run_partition(pipe, dl); | ||
410 | vsp1_dl_list_add_chain(pipe->dl, dl); | ||
297 | } | 411 | } |
298 | 412 | ||
413 | /* Complete, and commit the head display list. */ | ||
299 | vsp1_dl_list_commit(pipe->dl); | 414 | vsp1_dl_list_commit(pipe->dl); |
300 | pipe->dl = NULL; | 415 | pipe->dl = NULL; |
301 | 416 | ||
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index b757d2579d6c..fdee5a891e40 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c | |||
@@ -224,6 +224,9 @@ static void wpf_configure(struct vsp1_entity *entity, | |||
224 | /* Cropping. The partition algorithm can split the image into | 224 | /* Cropping. The partition algorithm can split the image into |
225 | * multiple slices. | 225 | * multiple slices. |
226 | */ | 226 | */ |
227 | if (pipe->partitions > 1) | ||
228 | width = pipe->partition.width; | ||
229 | |||
227 | vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | | 230 | vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | |
228 | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | | 231 | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | |
229 | (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); | 232 | (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); |
@@ -237,10 +240,31 @@ static void wpf_configure(struct vsp1_entity *entity, | |||
237 | /* Update the memory offsets based on flipping configuration. | 240 | /* Update the memory offsets based on flipping configuration. |
238 | * The destination addresses point to the locations where the | 241 | * The destination addresses point to the locations where the |
239 | * VSP starts writing to memory, which can be different corners | 242 | * VSP starts writing to memory, which can be different corners |
240 | * of the image depending on vertical flipping. Horizontal | 243 | * of the image depending on vertical flipping. |
241 | * flipping is handled through a line buffer and doesn't modify | ||
242 | * the start address. | ||
243 | */ | 244 | */ |
245 | if (pipe->partitions > 1) { | ||
246 | const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; | ||
247 | |||
248 | /* Horizontal flipping is handled through a line buffer | ||
249 | * and doesn't modify the start address, but still needs | ||
250 | * to be handled when image partitioning is in effect to | ||
251 | * order the partitions correctly. | ||
252 | */ | ||
253 | if (flip & BIT(WPF_CTRL_HFLIP)) | ||
254 | offset = format->width - pipe->partition.left | ||
255 | - pipe->partition.width; | ||
256 | else | ||
257 | offset = pipe->partition.left; | ||
258 | |||
259 | mem.addr[0] += offset * fmtinfo->bpp[0] / 8; | ||
260 | if (format->num_planes > 1) { | ||
261 | mem.addr[1] += offset / fmtinfo->hsub | ||
262 | * fmtinfo->bpp[1] / 8; | ||
263 | mem.addr[2] += offset / fmtinfo->hsub | ||
264 | * fmtinfo->bpp[2] / 8; | ||
265 | } | ||
266 | } | ||
267 | |||
244 | if (flip & BIT(WPF_CTRL_VFLIP)) { | 268 | if (flip & BIT(WPF_CTRL_VFLIP)) { |
245 | mem.addr[0] += (format->height - 1) | 269 | mem.addr[0] += (format->height - 1) |
246 | * format->plane_fmt[0].bytesperline; | 270 | * format->plane_fmt[0].bytesperline; |