diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2015-07-27 08:34:18 -0400 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2016-02-19 19:58:56 -0500 |
commit | ab334e137c31440e3a826e0d3c2753425f18641b (patch) | |
tree | 77671692d81686641543738a96d91c377bd1dd02 /drivers/gpu/drm/rcar-du/rcar_du_kms.c | |
parent | 2af0394409faec95e80d6061a8a9fe95565be358 (diff) |
drm: rcar-du: Move plane allocator to rcar_du_plane.c
The plane allocator is specific to DU planes and won't be used for
VSP-based planes, move it with the rest of the DU planes code where it
belongs.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Diffstat (limited to 'drivers/gpu/drm/rcar-du/rcar_du_kms.c')
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_kms.c | 276 |
1 files changed, 1 insertions, 275 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index a69d6075ceae..f5c00c0cd026 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c | |||
@@ -192,290 +192,16 @@ static void rcar_du_output_poll_changed(struct drm_device *dev) | |||
192 | * Atomic Check and Update | 192 | * Atomic Check and Update |
193 | */ | 193 | */ |
194 | 194 | ||
195 | /* | ||
196 | * Atomic hardware plane allocator | ||
197 | * | ||
198 | * The hardware plane allocator is solely based on the atomic plane states | ||
199 | * without keeping any external state to avoid races between .atomic_check() | ||
200 | * and .atomic_commit(). | ||
201 | * | ||
202 | * The core idea is to avoid using a free planes bitmask that would need to be | ||
203 | * shared between check and commit handlers with a collective knowledge based on | ||
204 | * the allocated hardware plane(s) for each KMS plane. The allocator then loops | ||
205 | * over all plane states to compute the free planes bitmask, allocates hardware | ||
206 | * planes based on that bitmask, and stores the result back in the plane states. | ||
207 | * | ||
208 | * For this to work we need to access the current state of planes not touched by | ||
209 | * the atomic update. To ensure that it won't be modified, we need to lock all | ||
210 | * planes using drm_atomic_get_plane_state(). This effectively serializes atomic | ||
211 | * updates from .atomic_check() up to completion (when swapping the states if | ||
212 | * the check step has succeeded) or rollback (when freeing the states if the | ||
213 | * check step has failed). | ||
214 | * | ||
215 | * Allocation is performed in the .atomic_check() handler and applied | ||
216 | * automatically when the core swaps the old and new states. | ||
217 | */ | ||
218 | |||
219 | static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane, | ||
220 | struct rcar_du_plane_state *new_state) | ||
221 | { | ||
222 | struct rcar_du_plane_state *cur_state; | ||
223 | |||
224 | cur_state = to_rcar_plane_state(plane->plane.state); | ||
225 | |||
226 | /* Lowering the number of planes doesn't strictly require reallocation | ||
227 | * as the extra hardware plane will be freed when committing, but doing | ||
228 | * so could lead to more fragmentation. | ||
229 | */ | ||
230 | if (!cur_state->format || | ||
231 | cur_state->format->planes != new_state->format->planes) | ||
232 | return true; | ||
233 | |||
234 | /* Reallocate hardware planes if the source has changed. */ | ||
235 | if (cur_state->source != new_state->source) | ||
236 | return true; | ||
237 | |||
238 | return false; | ||
239 | } | ||
240 | |||
241 | static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state) | ||
242 | { | ||
243 | unsigned int mask; | ||
244 | |||
245 | if (state->hwindex == -1) | ||
246 | return 0; | ||
247 | |||
248 | mask = 1 << state->hwindex; | ||
249 | if (state->format->planes == 2) | ||
250 | mask |= 1 << ((state->hwindex + 1) % 8); | ||
251 | |||
252 | return mask; | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and | ||
257 | * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or | ||
258 | * DU0/1 plane 1. | ||
259 | * | ||
260 | * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1, | ||
261 | * and allocate planes in reverse index order otherwise to ensure maximum | ||
262 | * availability of planes 0 and 1. | ||
263 | * | ||
264 | * The caller is responsible for ensuring that the requested source is | ||
265 | * compatible with the DU revision. | ||
266 | */ | ||
267 | static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane, | ||
268 | struct rcar_du_plane_state *state, | ||
269 | unsigned int free) | ||
270 | { | ||
271 | unsigned int num_planes = state->format->planes; | ||
272 | int fixed = -1; | ||
273 | int i; | ||
274 | |||
275 | if (state->source == RCAR_DU_PLANE_VSPD0) { | ||
276 | /* VSPD0 feeds plane 0 on DU0/1. */ | ||
277 | if (plane->group->index != 0) | ||
278 | return -EINVAL; | ||
279 | |||
280 | fixed = 0; | ||
281 | } else if (state->source == RCAR_DU_PLANE_VSPD1) { | ||
282 | /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */ | ||
283 | fixed = plane->group->index == 0 ? 1 : 0; | ||
284 | } | ||
285 | |||
286 | if (fixed >= 0) | ||
287 | return free & (1 << fixed) ? fixed : -EBUSY; | ||
288 | |||
289 | for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) { | ||
290 | if (!(free & (1 << i))) | ||
291 | continue; | ||
292 | |||
293 | if (num_planes == 1 || free & (1 << ((i + 1) % 8))) | ||
294 | break; | ||
295 | } | ||
296 | |||
297 | return i < 0 ? -EBUSY : i; | ||
298 | } | ||
299 | |||
300 | static int rcar_du_atomic_check(struct drm_device *dev, | 195 | static int rcar_du_atomic_check(struct drm_device *dev, |
301 | struct drm_atomic_state *state) | 196 | struct drm_atomic_state *state) |
302 | { | 197 | { |
303 | struct rcar_du_device *rcdu = dev->dev_private; | ||
304 | unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, }; | ||
305 | unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, }; | ||
306 | bool needs_realloc = false; | ||
307 | unsigned int groups = 0; | ||
308 | unsigned int i; | ||
309 | int ret; | 198 | int ret; |
310 | 199 | ||
311 | ret = drm_atomic_helper_check(dev, state); | 200 | ret = drm_atomic_helper_check(dev, state); |
312 | if (ret < 0) | 201 | if (ret < 0) |
313 | return ret; | 202 | return ret; |
314 | 203 | ||
315 | /* Check if hardware planes need to be reallocated. */ | 204 | return rcar_du_atomic_check_planes(dev, state); |
316 | for (i = 0; i < dev->mode_config.num_total_plane; ++i) { | ||
317 | struct rcar_du_plane_state *plane_state; | ||
318 | struct rcar_du_plane *plane; | ||
319 | unsigned int index; | ||
320 | |||
321 | if (!state->planes[i]) | ||
322 | continue; | ||
323 | |||
324 | plane = to_rcar_plane(state->planes[i]); | ||
325 | plane_state = to_rcar_plane_state(state->plane_states[i]); | ||
326 | |||
327 | dev_dbg(rcdu->dev, "%s: checking plane (%u,%u)\n", __func__, | ||
328 | plane->group->index, plane - plane->group->planes); | ||
329 | |||
330 | /* If the plane is being disabled we don't need to go through | ||
331 | * the full reallocation procedure. Just mark the hardware | ||
332 | * plane(s) as freed. | ||
333 | */ | ||
334 | if (!plane_state->format) { | ||
335 | dev_dbg(rcdu->dev, "%s: plane is being disabled\n", | ||
336 | __func__); | ||
337 | index = plane - plane->group->planes; | ||
338 | group_freed_planes[plane->group->index] |= 1 << index; | ||
339 | plane_state->hwindex = -1; | ||
340 | continue; | ||
341 | } | ||
342 | |||
343 | /* If the plane needs to be reallocated mark it as such, and | ||
344 | * mark the hardware plane(s) as free. | ||
345 | */ | ||
346 | if (rcar_du_plane_needs_realloc(plane, plane_state)) { | ||
347 | dev_dbg(rcdu->dev, "%s: plane needs reallocation\n", | ||
348 | __func__); | ||
349 | groups |= 1 << plane->group->index; | ||
350 | needs_realloc = true; | ||
351 | |||
352 | index = plane - plane->group->planes; | ||
353 | group_freed_planes[plane->group->index] |= 1 << index; | ||
354 | plane_state->hwindex = -1; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | if (!needs_realloc) | ||
359 | return 0; | ||
360 | |||
361 | /* Grab all plane states for the groups that need reallocation to ensure | ||
362 | * locking and avoid racy updates. This serializes the update operation, | ||
363 | * but there's not much we can do about it as that's the hardware | ||
364 | * design. | ||
365 | * | ||
366 | * Compute the used planes mask for each group at the same time to avoid | ||
367 | * looping over the planes separately later. | ||
368 | */ | ||
369 | while (groups) { | ||
370 | unsigned int index = ffs(groups) - 1; | ||
371 | struct rcar_du_group *group = &rcdu->groups[index]; | ||
372 | unsigned int used_planes = 0; | ||
373 | |||
374 | dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n", | ||
375 | __func__, index); | ||
376 | |||
377 | for (i = 0; i < group->num_planes; ++i) { | ||
378 | struct rcar_du_plane *plane = &group->planes[i]; | ||
379 | struct rcar_du_plane_state *plane_state; | ||
380 | struct drm_plane_state *s; | ||
381 | |||
382 | s = drm_atomic_get_plane_state(state, &plane->plane); | ||
383 | if (IS_ERR(s)) | ||
384 | return PTR_ERR(s); | ||
385 | |||
386 | /* If the plane has been freed in the above loop its | ||
387 | * hardware planes must not be added to the used planes | ||
388 | * bitmask. However, the current state doesn't reflect | ||
389 | * the free state yet, as we've modified the new state | ||
390 | * above. Use the local freed planes list to check for | ||
391 | * that condition instead. | ||
392 | */ | ||
393 | if (group_freed_planes[index] & (1 << i)) { | ||
394 | dev_dbg(rcdu->dev, | ||
395 | "%s: plane (%u,%u) has been freed, skipping\n", | ||
396 | __func__, plane->group->index, | ||
397 | plane - plane->group->planes); | ||
398 | continue; | ||
399 | } | ||
400 | |||
401 | plane_state = to_rcar_plane_state(plane->plane.state); | ||
402 | used_planes |= rcar_du_plane_hwmask(plane_state); | ||
403 | |||
404 | dev_dbg(rcdu->dev, | ||
405 | "%s: plane (%u,%u) uses %u hwplanes (index %d)\n", | ||
406 | __func__, plane->group->index, | ||
407 | plane - plane->group->planes, | ||
408 | plane_state->format ? | ||
409 | plane_state->format->planes : 0, | ||
410 | plane_state->hwindex); | ||
411 | } | ||
412 | |||
413 | group_free_planes[index] = 0xff & ~used_planes; | ||
414 | groups &= ~(1 << index); | ||
415 | |||
416 | dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", | ||
417 | __func__, index, group_free_planes[index]); | ||
418 | } | ||
419 | |||
420 | /* Reallocate hardware planes for each plane that needs it. */ | ||
421 | for (i = 0; i < dev->mode_config.num_total_plane; ++i) { | ||
422 | struct rcar_du_plane_state *plane_state; | ||
423 | struct rcar_du_plane *plane; | ||
424 | unsigned int crtc_planes; | ||
425 | unsigned int free; | ||
426 | int idx; | ||
427 | |||
428 | if (!state->planes[i]) | ||
429 | continue; | ||
430 | |||
431 | plane = to_rcar_plane(state->planes[i]); | ||
432 | plane_state = to_rcar_plane_state(state->plane_states[i]); | ||
433 | |||
434 | dev_dbg(rcdu->dev, "%s: allocating plane (%u,%u)\n", __func__, | ||
435 | plane->group->index, plane - plane->group->planes); | ||
436 | |||
437 | /* Skip planes that are being disabled or don't need to be | ||
438 | * reallocated. | ||
439 | */ | ||
440 | if (!plane_state->format || | ||
441 | !rcar_du_plane_needs_realloc(plane, plane_state)) | ||
442 | continue; | ||
443 | |||
444 | /* Try to allocate the plane from the free planes currently | ||
445 | * associated with the target CRTC to avoid restarting the CRTC | ||
446 | * group and thus minimize flicker. If it fails fall back to | ||
447 | * allocating from all free planes. | ||
448 | */ | ||
449 | crtc_planes = to_rcar_crtc(plane_state->state.crtc)->index % 2 | ||
450 | ? plane->group->dptsr_planes | ||
451 | : ~plane->group->dptsr_planes; | ||
452 | free = group_free_planes[plane->group->index]; | ||
453 | |||
454 | idx = rcar_du_plane_hwalloc(plane, plane_state, | ||
455 | free & crtc_planes); | ||
456 | if (idx < 0) | ||
457 | idx = rcar_du_plane_hwalloc(plane, plane_state, | ||
458 | free); | ||
459 | if (idx < 0) { | ||
460 | dev_dbg(rcdu->dev, "%s: no available hardware plane\n", | ||
461 | __func__); | ||
462 | return idx; | ||
463 | } | ||
464 | |||
465 | dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n", | ||
466 | __func__, plane_state->format->planes, idx); | ||
467 | |||
468 | plane_state->hwindex = idx; | ||
469 | |||
470 | group_free_planes[plane->group->index] &= | ||
471 | ~rcar_du_plane_hwmask(plane_state); | ||
472 | |||
473 | dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", | ||
474 | __func__, plane->group->index, | ||
475 | group_free_planes[plane->group->index]); | ||
476 | } | ||
477 | |||
478 | return 0; | ||
479 | } | 205 | } |
480 | 206 | ||
481 | struct rcar_du_commit { | 207 | struct rcar_du_commit { |