/** * drm_atomic_plane_check - check plane state * @plane: plane to check * @state: plane state to check * * Provides core sanity checks for plane state. * * RETURNS: * Zero on success, error code on failure */ static int drm_atomic_plane_check(struct drm_plane *plane, struct drm_plane_state *state) { unsigned int fb_width, fb_height; int ret; /* either *both* CRTC and FB must be set, or neither */ if (WARN_ON(state->crtc && !state->fb)) { DRM_DEBUG_ATOMIC("CRTC set but no FB\n"); return -EINVAL; } else if (WARN_ON(state->fb && !state->crtc)) { DRM_DEBUG_ATOMIC("FB set but no CRTC\n"); return -EINVAL; } /* if disabled, we don't care about the rest of the state: */ if (!state->crtc) return 0; /* Check whether this plane is usable on this CRTC */ if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { DRM_DEBUG_ATOMIC("Invalid crtc for plane\n"); return -EINVAL; } /* Check whether this plane supports the fb pixel format. */ ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format); if (ret) { DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", drm_get_format_name(state->fb->pixel_format)); return ret; } /* Give drivers some help against integer overflows */ if (state->crtc_w > INT_MAX || state->crtc_x > INT_MAX - (int32_t) state->crtc_w || state->crtc_h > INT_MAX || state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { DRM_DEBUG_ATOMIC("Invalid CRTC coordinates %ux%u+%d+%d\n", state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y); return -ERANGE; } fb_width = state->fb->width << 16; fb_height = state->fb->height << 16; /* Make sure source coordinates are inside the fb. */ if (state->src_w > fb_width || state->src_x > fb_width - state->src_w || state->src_h > fb_height || state->src_y > fb_height - state->src_h) { DRM_DEBUG_ATOMIC("Invalid source coordinates " "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); return -ENOSPC; }
int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder, enum exynos_drm_output_type out_type) { struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev, out_type); if (IS_ERR(crtc)) return PTR_ERR(crtc); encoder->possible_crtcs = drm_crtc_mask(&crtc->base); return 0; }
static int __setplane_check(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int32_t crtc_x, int32_t crtc_y, uint32_t crtc_w, uint32_t crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { int ret; /* Check whether this plane is usable on this CRTC */ if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { DRM_DEBUG_KMS("Invalid crtc for plane\n"); return -EINVAL; } /* Check whether this plane supports the fb pixel format. */ ret = drm_plane_check_pixel_format(plane, fb->format->format, fb->modifier); if (ret) { struct drm_format_name_buf format_name; DRM_DEBUG_KMS("Invalid pixel format %s, modifier 0x%llx\n", drm_get_format_name(fb->format->format, &format_name), fb->modifier); return ret; } /* Give drivers some help against integer overflows */ if (crtc_w > INT_MAX || crtc_x > INT_MAX - (int32_t) crtc_w || crtc_h > INT_MAX || crtc_y > INT_MAX - (int32_t) crtc_h) { DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", crtc_w, crtc_h, crtc_x, crtc_y); return -ERANGE; } ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, fb); if (ret) return ret; return 0; }
/** * drm_simple_display_pipe_init - Initialize a simple display pipeline * @dev: DRM device * @pipe: simple display pipe object to initialize * @funcs: callbacks for the display pipe (optional) * @formats: array of supported formats (DRM_FORMAT\_\*) * @format_count: number of elements in @formats * @format_modifiers: array of formats modifiers * @connector: connector to attach and register (optional) * * Sets up a display pipeline which consist of a really simple * plane-crtc-encoder pipe. * * If a connector is supplied, the pipe will be coupled with the provided * connector. You may supply a NULL connector when using drm bridges, that * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). * * Teardown of a simple display pipe is all handled automatically by the drm * core through calling drm_mode_config_cleanup(). Drivers afterwards need to * release the memory for the structure themselves. * * Returns: * Zero on success, negative error code on failure. */ int drm_simple_display_pipe_init(struct drm_device *dev, struct drm_simple_display_pipe *pipe, const struct drm_simple_display_pipe_funcs *funcs, const uint32_t *formats, unsigned int format_count, const uint64_t *format_modifiers, struct drm_connector *connector) { struct drm_encoder *encoder = &pipe->encoder; struct drm_plane *plane = &pipe->plane; struct drm_crtc *crtc = &pipe->crtc; int ret; pipe->connector = connector; pipe->funcs = funcs; drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs); ret = drm_universal_plane_init(dev, plane, 0, &drm_simple_kms_plane_funcs, formats, format_count, format_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) return ret; drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs); ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &drm_simple_kms_crtc_funcs, NULL); if (ret) return ret; encoder->possible_crtcs = drm_crtc_mask(crtc); ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); if (ret || !connector) return ret; return drm_connector_attach_encoder(connector, encoder); }
/** * drm_atomic_helper_commit - commit validated state object * @dev: DRM device * @state: the driver state object * @nonblock: nonblocking commit * * This function commits a with drm_atomic_helper_check() pre-validated state * object. This can still fail when e.g. the framebuffer reservation fails. * * RETURNS * Zero for success or -errno. */ int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock) { struct msm_drm_private *priv = dev->dev_private; struct msm_commit *c; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; struct drm_plane *plane; struct drm_plane_state *plane_state; int i, ret; ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) return ret; c = commit_init(state); if (!c) { ret = -ENOMEM; goto error; } /* * Figure out what crtcs we have: */ for_each_crtc_in_state(state, crtc, crtc_state, i) c->crtc_mask |= drm_crtc_mask(crtc); /* * Figure out what fence to wait for: */ for_each_plane_in_state(state, plane, plane_state, i) { if ((plane->state->fb != plane_state->fb) && plane_state->fb) { struct drm_gem_object *obj = msm_framebuffer_bo(plane_state->fb, 0); struct msm_gem_object *msm_obj = to_msm_bo(obj); struct dma_fence *fence = reservation_object_get_excl_rcu(msm_obj->resv); drm_atomic_set_fence_for_plane(plane_state, fence); } } /* * Wait for pending updates on any of the same crtc's and then * mark our set of crtc's as busy: */ ret = start_atomic(dev->dev_private, c->crtc_mask); if (ret) { kfree(c); goto error; } /* * This is the point of no return - everything below never fails except * when the hw goes bonghits. Which means we can commit the new state on * the software side now. */ drm_atomic_helper_swap_state(state, true); /* swap driver private state while still holding state_lock */ if (to_kms_state(state)->state) priv->kms->funcs->swap_state(priv->kms, state); /* * Everything below can be run asynchronously without the need to grab * any modeset locks at all under one conditions: It must be guaranteed * that the asynchronous work has either been cancelled (if the driver * supports it, which at least requires that the framebuffers get * cleaned up with drm_atomic_helper_cleanup_planes()) or completed * before the new state gets committed on the software side with * drm_atomic_helper_swap_state(). * * This scheme allows new atomic state updates to be prepared and * checked in parallel to the asynchronous completion of the previous * update. Which is important since compositors need to figure out the * composition of the next frame right after having submitted the * current layout. */ drm_atomic_state_get(state); if (nonblock) { queue_work(priv->atomic_wq, &c->work); return 0; } complete_commit(c, false); return 0; error: drm_atomic_helper_cleanup_planes(dev, state); return ret; }
struct rockchip_rgb *rockchip_rgb_init(struct device *dev, struct drm_crtc *crtc, struct drm_device *drm_dev) { struct rockchip_rgb *rgb; struct drm_encoder *encoder; struct device_node *port, *endpoint; u32 endpoint_id; int ret = 0, child_count = 0; struct drm_panel *panel; struct drm_bridge *bridge; rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL); if (!rgb) return ERR_PTR(-ENOMEM); rgb->dev = dev; rgb->drm_dev = drm_dev; port = of_graph_get_port_by_id(dev->of_node, 0); if (!port) return ERR_PTR(-EINVAL); for_each_child_of_node(port, endpoint) { if (of_property_read_u32(endpoint, "reg", &endpoint_id)) endpoint_id = 0; if (rockchip_drm_endpoint_is_subdriver(endpoint) > 0) continue; child_count++; ret = drm_of_find_panel_or_bridge(dev->of_node, 0, endpoint_id, &panel, &bridge); if (!ret) { of_node_put(endpoint); break; } } of_node_put(port); /* if the rgb output is not connected to anything, just return */ if (!child_count) return NULL; if (ret < 0) { if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "failed to find panel or bridge %d\n", ret); return ERR_PTR(ret); } encoder = &rgb->encoder; encoder->possible_crtcs = drm_crtc_mask(crtc); ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); if (ret < 0) { DRM_DEV_ERROR(drm_dev->dev, "failed to initialize encoder: %d\n", ret); return ERR_PTR(ret); } drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs); if (panel) { bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS); if (IS_ERR(bridge)) return ERR_CAST(bridge); } rgb->bridge = bridge; ret = drm_bridge_attach(encoder, rgb->bridge, NULL); if (ret) { DRM_DEV_ERROR(drm_dev->dev, "failed to attach bridge: %d\n", ret); goto err_free_encoder; } return rgb; err_free_encoder: drm_encoder_cleanup(encoder); return ERR_PTR(ret); }
/** * drm_crtc_init_with_planes - Initialise a new CRTC object with * specified primary and cursor planes. * @dev: DRM device * @crtc: CRTC object to init * @primary: Primary plane for CRTC * @cursor: Cursor plane for CRTC * @funcs: callbacks for the new CRTC * @name: printf style format string for the CRTC name, or NULL for default name * * Inits a new object created as base part of a driver crtc object. Drivers * should use this function instead of drm_crtc_init(), which is only provided * for backwards compatibility with drivers which do not yet support universal * planes). For really simple hardware which has only 1 plane look at * drm_simple_display_pipe_init() instead. * * Returns: * Zero on success, error code on failure. */ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, struct drm_plane *cursor, const struct drm_crtc_funcs *funcs, const char *name, ...) { struct drm_mode_config *config = &dev->mode_config; int ret; WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY); WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR); /* crtc index is used with 32bit bitmasks */ if (WARN_ON(config->num_crtc >= 32)) return -EINVAL; WARN_ON(drm_drv_uses_atomic_modeset(dev) && (!funcs->atomic_destroy_state || !funcs->atomic_duplicate_state)); crtc->dev = dev; crtc->funcs = funcs; INIT_LIST_HEAD(&crtc->commit_list); spin_lock_init(&crtc->commit_lock); drm_modeset_lock_init(&crtc->mutex); ret = drm_mode_object_add(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) return ret; if (name) { va_list ap; va_start(ap, name); crtc->name = kvasprintf(GFP_KERNEL, name, ap); va_end(ap); } else { crtc->name = kasprintf(GFP_KERNEL, "crtc-%d", drm_num_crtcs(dev)); } if (!crtc->name) { drm_mode_object_unregister(dev, &crtc->base); return -ENOMEM; } crtc->fence_context = dma_fence_context_alloc(1); spin_lock_init(&crtc->fence_lock); snprintf(crtc->timeline_name, sizeof(crtc->timeline_name), "CRTC:%d-%s", crtc->base.id, crtc->name); crtc->base.properties = &crtc->properties; list_add_tail(&crtc->head, &config->crtc_list); crtc->index = config->num_crtc++; crtc->primary = primary; crtc->cursor = cursor; if (primary && !primary->possible_crtcs) primary->possible_crtcs = drm_crtc_mask(crtc); if (cursor && !cursor->possible_crtcs) cursor->possible_crtcs = drm_crtc_mask(crtc); ret = drm_crtc_crc_init(crtc); if (ret) { drm_mode_object_unregister(dev, &crtc->base); return ret; } if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { drm_object_attach_property(&crtc->base, config->prop_active, 0); drm_object_attach_property(&crtc->base, config->prop_mode_id, 0); drm_object_attach_property(&crtc->base, config->prop_out_fence_ptr, 0); drm_object_attach_property(&crtc->base, config->prop_vrr_enabled, 0); } return 0; }
/** * drm_atomic_plane_check - check plane state * @old_plane_state: old plane state to check * @new_plane_state: new plane state to check * * Provides core sanity checks for plane state. * * RETURNS: * Zero on success, error code on failure */ static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state, const struct drm_plane_state *new_plane_state) { struct drm_plane *plane = new_plane_state->plane; struct drm_crtc *crtc = new_plane_state->crtc; const struct drm_framebuffer *fb = new_plane_state->fb; unsigned int fb_width, fb_height; struct drm_mode_rect *clips; uint32_t num_clips; int ret; /* either *both* CRTC and FB must be set, or neither */ if (crtc && !fb) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] CRTC set but no FB\n", plane->base.id, plane->name); return -EINVAL; } else if (fb && !crtc) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] FB set but no CRTC\n", plane->base.id, plane->name); return -EINVAL; } /* if disabled, we don't care about the rest of the state: */ if (!crtc) return 0; /* Check whether this plane is usable on this CRTC */ if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { DRM_DEBUG_ATOMIC("Invalid [CRTC:%d:%s] for [PLANE:%d:%s]\n", crtc->base.id, crtc->name, plane->base.id, plane->name); return -EINVAL; } /* Check whether this plane supports the fb pixel format. */ ret = drm_plane_check_pixel_format(plane, fb->format->format, fb->modifier); if (ret) { struct drm_format_name_buf format_name; DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid pixel format %s, modifier 0x%llx\n", plane->base.id, plane->name, drm_get_format_name(fb->format->format, &format_name), fb->modifier); return ret; } /* Give drivers some help against integer overflows */ if (new_plane_state->crtc_w > INT_MAX || new_plane_state->crtc_x > INT_MAX - (int32_t) new_plane_state->crtc_w || new_plane_state->crtc_h > INT_MAX || new_plane_state->crtc_y > INT_MAX - (int32_t) new_plane_state->crtc_h) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid CRTC coordinates %ux%u+%d+%d\n", plane->base.id, plane->name, new_plane_state->crtc_w, new_plane_state->crtc_h, new_plane_state->crtc_x, new_plane_state->crtc_y); return -ERANGE; } fb_width = fb->width << 16; fb_height = fb->height << 16; /* Make sure source coordinates are inside the fb. */ if (new_plane_state->src_w > fb_width || new_plane_state->src_x > fb_width - new_plane_state->src_w || new_plane_state->src_h > fb_height || new_plane_state->src_y > fb_height - new_plane_state->src_h) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid source coordinates " "%u.%06ux%u.%06u+%u.%06u+%u.%06u (fb %ux%u)\n", plane->base.id, plane->name, new_plane_state->src_w >> 16, ((new_plane_state->src_w & 0xffff) * 15625) >> 10, new_plane_state->src_h >> 16, ((new_plane_state->src_h & 0xffff) * 15625) >> 10, new_plane_state->src_x >> 16, ((new_plane_state->src_x & 0xffff) * 15625) >> 10, new_plane_state->src_y >> 16, ((new_plane_state->src_y & 0xffff) * 15625) >> 10, fb->width, fb->height); return -ENOSPC; }
static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, struct ipu_client_platformdata *pdata, struct drm_device *drm) { struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); int dp = -EINVAL; int ret; ret = ipu_get_resources(ipu_crtc, pdata); if (ret) { dev_err(ipu_crtc->dev, "getting resources failed with %d.\n", ret); return ret; } if (pdata->dp >= 0) dp = IPU_DP_FLOW_SYNC_BG; ipu_crtc->plane[0] = ipu_plane_init(drm, ipu, pdata->dma[0], dp, 0, DRM_PLANE_TYPE_PRIMARY); if (IS_ERR(ipu_crtc->plane[0])) { ret = PTR_ERR(ipu_crtc->plane[0]); goto err_put_resources; } ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc, &ipu_crtc->plane[0]->base, &ipu_crtc_helper_funcs, pdata->of_node); if (ret) { dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); goto err_put_resources; } ret = ipu_plane_get_resources(ipu_crtc->plane[0]); if (ret) { dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n", ret); goto err_remove_crtc; } /* If this crtc is using the DP, add an overlay plane */ if (pdata->dp >= 0 && pdata->dma[1] > 0) { ipu_crtc->plane[1] = ipu_plane_init(drm, ipu, pdata->dma[1], IPU_DP_FLOW_SYNC_FG, drm_crtc_mask(&ipu_crtc->base), DRM_PLANE_TYPE_OVERLAY); if (IS_ERR(ipu_crtc->plane[1])) ipu_crtc->plane[1] = NULL; } ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]); ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, "imx_drm", ipu_crtc); if (ret < 0) { dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); goto err_put_plane_res; } /* Only enable IRQ when we actually need it to trigger work. */ disable_irq(ipu_crtc->irq); ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip"); return 0; err_put_plane_res: ipu_plane_put_resources(ipu_crtc->plane[0]); err_remove_crtc: imx_drm_remove_crtc(ipu_crtc->imx_crtc); err_put_resources: ipu_put_resources(ipu_crtc); return ret; }