On Mon, 2011-11-14 at 12:21 -0800, Jesse Barnes wrote: > Planes are a bit like half-CRTCs. They have a location and fb, but > don't drive outputs directly. Add support for handling them to the core > KMS code. Out of curiosity, lets say you have a *really* stupid hardware overlay that can't do scaling (or even, has limited scaling capabilities), should we provide some way to expose this to userspace? I've recently looked at the hardware overlay on recent NVIDIA GPUs with the intention of implementing this API in nouveau. However, while there is indeed an overlay, it only accepts a couple of RGB formats and does simple clipping - and *no* scaling that I've found so far. >From traces of VDPAU, NVIDIA themselves appear to do CSC and scaling via some other method before passing the frame off to the overlay. Rather disappointing, and seemingly not of too much use. But, I'd like to expose what functionality there is in any case. Ben. > > v2: fix ABI of get_plane - move format_type_ptr to the end > > Acked-by: Alan Cox <alan@xxxxxxxxxxxxxxxxxxx> > Reviewed-by: Rob Clark <rob.clark@xxxxxxxxxx> > Reviewed-by: Daniel Vetter <daniel.vetter@xxxxxxxx> > Signed-off-by: Jesse Barnes <jbarnes@xxxxxxxxxxxxxxxx> > --- > drivers/gpu/drm/drm_crtc.c | 257 +++++++++++++++++++++++++++++++++++++++++++- > drivers/gpu/drm/drm_drv.c | 3 + > include/drm/drm.h | 3 + > include/drm/drm_crtc.h | 75 +++++++++++++- > include/drm/drm_mode.h | 33 ++++++ > 5 files changed, 368 insertions(+), 3 deletions(-) > > diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c > index fe738f0..804ef12 100644 > --- a/drivers/gpu/drm/drm_crtc.c > +++ b/drivers/gpu/drm/drm_crtc.c > @@ -321,6 +321,7 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) > { > struct drm_device *dev = fb->dev; > struct drm_crtc *crtc; > + struct drm_plane *plane; > struct drm_mode_set set; > int ret; > > @@ -337,6 +338,15 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) > } > } > > + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > + if (plane->fb == fb) { > + /* should turn off the crtc */ > + ret = plane->funcs->disable_plane(plane); > + if (ret) > + DRM_ERROR("failed to disable plane with busy fb\n"); > + } > + } > + > drm_mode_object_put(dev, &fb->base); > list_del(&fb->head); > dev->mode_config.num_fb--; > @@ -535,6 +545,50 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) > } > EXPORT_SYMBOL(drm_encoder_cleanup); > > +int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, > + unsigned long possible_crtcs, > + const struct drm_plane_funcs *funcs, > + uint32_t *formats, uint32_t format_count) > +{ > + mutex_lock(&dev->mode_config.mutex); > + > + plane->dev = dev; > + drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); > + plane->funcs = funcs; > + plane->format_types = kmalloc(sizeof(uint32_t) * format_count, > + GFP_KERNEL); > + if (!plane->format_types) { > + DRM_DEBUG_KMS("out of memory when allocating plane\n"); > + drm_mode_object_put(dev, &plane->base); > + return -ENOMEM; > + } > + > + memcpy(plane->format_types, formats, format_count); > + plane->format_count = format_count; > + plane->possible_crtcs = possible_crtcs; > + > + list_add_tail(&plane->head, &dev->mode_config.plane_list); > + dev->mode_config.num_plane++; > + > + mutex_unlock(&dev->mode_config.mutex); > + > + return 0; > +} > +EXPORT_SYMBOL(drm_plane_init); > + > +void drm_plane_cleanup(struct drm_plane *plane) > +{ > + struct drm_device *dev = plane->dev; > + > + mutex_lock(&dev->mode_config.mutex); > + kfree(plane->format_types); > + drm_mode_object_put(dev, &plane->base); > + list_del(&plane->head); > + dev->mode_config.num_plane--; > + mutex_unlock(&dev->mode_config.mutex); > +} > +EXPORT_SYMBOL(drm_plane_cleanup); > + > /** > * drm_mode_create - create a new display mode > * @dev: DRM device > @@ -866,6 +920,7 @@ void drm_mode_config_init(struct drm_device *dev) > INIT_LIST_HEAD(&dev->mode_config.encoder_list); > INIT_LIST_HEAD(&dev->mode_config.property_list); > INIT_LIST_HEAD(&dev->mode_config.property_blob_list); > + INIT_LIST_HEAD(&dev->mode_config.plane_list); > idr_init(&dev->mode_config.crtc_idr); > > mutex_lock(&dev->mode_config.mutex); > @@ -942,6 +997,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) > struct drm_encoder *encoder, *enct; > struct drm_framebuffer *fb, *fbt; > struct drm_property *property, *pt; > + struct drm_plane *plane, *plt; > > list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, > head) { > @@ -966,6 +1022,10 @@ void drm_mode_config_cleanup(struct drm_device *dev) > crtc->funcs->destroy(crtc); > } > > + list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, > + head) { > + plane->funcs->destroy(plane); > + } > } > EXPORT_SYMBOL(drm_mode_config_cleanup); > > @@ -1466,6 +1526,197 @@ out: > } > > /** > + * drm_mode_getplane_res - get plane info > + * @dev: DRM device > + * @data: ioctl data > + * @file_priv: DRM file info > + * > + * Return an plane count and set of IDs. > + */ > +int drm_mode_getplane_res(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_mode_get_plane_res *plane_resp = data; > + struct drm_mode_config *config; > + struct drm_plane *plane; > + uint32_t __user *plane_ptr; > + int copied = 0, ret = 0; > + > + if (!drm_core_check_feature(dev, DRIVER_MODESET)) > + return -EINVAL; > + > + mutex_lock(&dev->mode_config.mutex); > + config = &dev->mode_config; > + > + /* > + * This ioctl is called twice, once to determine how much space is > + * needed, and the 2nd time to fill it. > + */ > + if (config->num_plane && > + (plane_resp->count_planes >= config->num_plane)) { > + plane_ptr = (uint32_t *)(unsigned long)plane_resp->plane_id_ptr; > + > + list_for_each_entry(plane, &config->plane_list, head) { > + if (put_user(plane->base.id, plane_ptr + copied)) { > + ret = -EFAULT; > + goto out; > + } > + copied++; > + } > + } > + plane_resp->count_planes = config->num_plane; > + > +out: > + mutex_unlock(&dev->mode_config.mutex); > + return ret; > +} > + > +/** > + * drm_mode_getplane - get plane info > + * @dev: DRM device > + * @data: ioctl data > + * @file_priv: DRM file info > + * > + * Return plane info, including formats supported, gamma size, any > + * current fb, etc. > + */ > +int drm_mode_getplane(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_mode_get_plane *plane_resp = data; > + struct drm_mode_object *obj; > + struct drm_plane *plane; > + uint32_t __user *format_ptr; > + int ret = 0; > + > + if (!drm_core_check_feature(dev, DRIVER_MODESET)) > + return -EINVAL; > + > + mutex_lock(&dev->mode_config.mutex); > + obj = drm_mode_object_find(dev, plane_resp->plane_id, > + DRM_MODE_OBJECT_PLANE); > + if (!obj) { > + ret = -ENOENT; > + goto out; > + } > + plane = obj_to_plane(obj); > + > + if (plane->crtc) > + plane_resp->crtc_id = plane->crtc->base.id; > + else > + plane_resp->crtc_id = 0; > + > + if (plane->fb) > + plane_resp->fb_id = plane->fb->base.id; > + else > + plane_resp->fb_id = 0; > + > + plane_resp->plane_id = plane->base.id; > + plane_resp->possible_crtcs = plane->possible_crtcs; > + plane_resp->gamma_size = plane->gamma_size; > + > + /* > + * This ioctl is called twice, once to determine how much space is > + * needed, and the 2nd time to fill it. > + */ > + if (plane->format_count && > + (plane_resp->count_format_types >= plane->format_count)) { > + format_ptr = (uint32_t *)(unsigned long)plane_resp->format_type_ptr; > + if (copy_to_user(format_ptr, > + plane->format_types, > + sizeof(uint32_t) * plane->format_count)) { > + ret = -EFAULT; > + goto out; > + } > + } > + plane_resp->count_format_types = plane->format_count; > + > +out: > + mutex_unlock(&dev->mode_config.mutex); > + return ret; > +} > + > +/** > + * drm_mode_setplane - set up or tear down an plane > + * @dev: DRM device > + * @data: ioctl data* > + * @file_prive: DRM file info > + * > + * Set plane info, including placement, fb, scaling, and other factors. > + * Or pass a NULL fb to disable. > + */ > +int drm_mode_setplane(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_mode_set_plane *plane_req = data; > + struct drm_mode_object *obj; > + struct drm_plane *plane; > + struct drm_crtc *crtc; > + struct drm_framebuffer *fb; > + int ret = 0; > + > + if (!drm_core_check_feature(dev, DRIVER_MODESET)) > + return -EINVAL; > + > + mutex_lock(&dev->mode_config.mutex); > + > + /* > + * First, find the plane, crtc, and fb objects. If not available, > + * we don't bother to call the driver. > + */ > + obj = drm_mode_object_find(dev, plane_req->plane_id, > + DRM_MODE_OBJECT_PLANE); > + if (!obj) { > + DRM_DEBUG_KMS("Unknown plane ID %d\n", > + plane_req->plane_id); > + ret = -ENOENT; > + goto out; > + } > + plane = obj_to_plane(obj); > + > + /* No fb means shut it down */ > + if (!plane_req->fb_id) { > + plane->funcs->disable_plane(plane); > + goto out; > + } > + > + obj = drm_mode_object_find(dev, plane_req->crtc_id, > + DRM_MODE_OBJECT_CRTC); > + if (!obj) { > + DRM_DEBUG_KMS("Unknown crtc ID %d\n", > + plane_req->crtc_id); > + ret = -ENOENT; > + goto out; > + } > + crtc = obj_to_crtc(obj); > + > + obj = drm_mode_object_find(dev, plane_req->fb_id, > + DRM_MODE_OBJECT_FB); > + if (!obj) { > + DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", > + plane_req->fb_id); > + ret = -ENOENT; > + goto out; > + } > + fb = obj_to_fb(obj); > + > + ret = plane->funcs->update_plane(plane, crtc, fb, > + plane_req->crtc_x, plane_req->crtc_y, > + plane_req->crtc_w, plane_req->crtc_h, > + plane_req->src_x, plane_req->src_y, > + plane_req->src_w, plane_req->src_h); > + if (!ret) { > + plane->crtc = crtc; > + plane->fb = fb; > + } > + > +out: > + mutex_unlock(&dev->mode_config.mutex); > + > + return ret; > +} > + > +/** > * drm_mode_setcrtc - set CRTC configuration > * @inode: inode from the ioctl > * @filp: file * from the ioctl > @@ -1688,11 +1939,13 @@ int drm_mode_addfb(struct drm_device *dev, > return -EINVAL; > > if ((config->min_width > r->width) || (r->width > config->max_width)) { > - DRM_ERROR("mode new framebuffer width not within limits\n"); > + DRM_ERROR("bad framebuffer width %d, should be >= %d && <= %d\n", > + r->width, config->min_width, config->max_width); > return -EINVAL; > } > if ((config->min_height > r->height) || (r->height > config->max_height)) { > - DRM_ERROR("mode new framebuffer height not within limits\n"); > + DRM_ERROR("bad framebuffer height %d, should be >= %d && <= %d\n", > + r->height, config->min_height, config->max_height); > return -EINVAL; > } > > diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c > index 7a87e08..d782bd1 100644 > --- a/drivers/gpu/drm/drm_drv.c > +++ b/drivers/gpu/drm/drm_drv.c > @@ -135,8 +135,11 @@ static struct drm_ioctl_desc drm_ioctls[] = { > DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED), > > DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), > DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER|DRM_UNLOCKED), > DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED), > diff --git a/include/drm/drm.h b/include/drm/drm.h > index 4be33b4..2897967 100644 > --- a/include/drm/drm.h > +++ b/include/drm/drm.h > @@ -714,6 +714,9 @@ struct drm_get_cap { > #define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb) > #define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb) > #define DRM_IOCTL_MODE_DESTROY_DUMB DRM_IOWR(0xB4, struct drm_mode_destroy_dumb) > +#define DRM_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xB5, struct drm_mode_get_plane_res) > +#define DRM_IOCTL_MODE_GETPLANE DRM_IOWR(0xB6, struct drm_mode_get_plane) > +#define DRM_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct drm_mode_set_plane) > > /** > * Device specific ioctls should only be in their respective headers > diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h > index 8020798..e20867e 100644 > --- a/include/drm/drm_crtc.h > +++ b/include/drm/drm_crtc.h > @@ -44,6 +44,7 @@ struct drm_framebuffer; > #define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 > #define DRM_MODE_OBJECT_FB 0xfbfbfbfb > #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb > +#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee > > struct drm_mode_object { > uint32_t id; > @@ -278,6 +279,7 @@ struct drm_crtc; > struct drm_connector; > struct drm_encoder; > struct drm_pending_vblank_event; > +struct drm_plane; > > /** > * drm_crtc_funcs - control CRTCs for a given device > @@ -536,6 +538,62 @@ struct drm_connector { > }; > > /** > + * drm_plane_funcs - driver plane control functions > + * @update_plane: update the plane configuration > + * @disable_plane: shut down the plane > + * @destroy: clean up plane resources > + */ > +struct drm_plane_funcs { > + int (*update_plane)(struct drm_plane *plane, > + struct drm_crtc *crtc, struct drm_framebuffer *fb, > + int crtc_x, int crtc_y, > + unsigned int crtc_w, unsigned int crtc_h, > + uint32_t src_x, uint32_t src_y, > + uint32_t src_w, uint32_t src_h); > + int (*disable_plane)(struct drm_plane *plane); > + void (*destroy)(struct drm_plane *plane); > +}; > + > +/** > + * drm_plane - central DRM plane control structure > + * @dev: DRM device this plane belongs to > + * @head: for list management > + * @base: base mode object > + * @possible_crtcs: pipes this plane can be bound to > + * @format_types: array of formats supported by this plane > + * @format_count: number of formats supported > + * @crtc: currently bound CRTC > + * @fb: currently bound fb > + * @gamma_size: size of gamma table > + * @gamma_store: gamma correction table > + * @enabled: enabled flag > + * @funcs: helper functions > + * @helper_private: storage for drver layer > + */ > +struct drm_plane { > + struct drm_device *dev; > + struct list_head head; > + > + struct drm_mode_object base; > + > + uint32_t possible_crtcs; > + uint32_t *format_types; > + uint32_t format_count; > + > + struct drm_crtc *crtc; > + struct drm_framebuffer *fb; > + > + /* CRTC gamma size for reporting to userspace */ > + uint32_t gamma_size; > + uint16_t *gamma_store; > + > + bool enabled; > + > + const struct drm_plane_funcs *funcs; > + void *helper_private; > +}; > + > +/** > * struct drm_mode_set > * > * Represents a single crtc the connectors that it drives with what mode > @@ -589,6 +647,8 @@ struct drm_mode_config { > struct list_head connector_list; > int num_encoder; > struct list_head encoder_list; > + int num_plane; > + struct list_head plane_list; > > int num_crtc; > struct list_head crtc_list; > @@ -641,6 +701,7 @@ struct drm_mode_config { > #define obj_to_fb(x) container_of(x, struct drm_framebuffer, base) > #define obj_to_property(x) container_of(x, struct drm_property, base) > #define obj_to_blob(x) container_of(x, struct drm_property_blob, base) > +#define obj_to_plane(x) container_of(x, struct drm_plane, base) > > > extern void drm_crtc_init(struct drm_device *dev, > @@ -660,6 +721,13 @@ extern void drm_encoder_init(struct drm_device *dev, > const struct drm_encoder_funcs *funcs, > int encoder_type); > > +extern int drm_plane_init(struct drm_device *dev, > + struct drm_plane *plane, > + unsigned long possible_crtcs, > + const struct drm_plane_funcs *funcs, > + uint32_t *formats, uint32_t format_count); > +extern void drm_plane_cleanup(struct drm_plane *plane); > + > extern void drm_encoder_cleanup(struct drm_encoder *encoder); > > extern char *drm_get_connector_name(struct drm_connector *connector); > @@ -753,13 +821,18 @@ extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, > /* IOCTLs */ > extern int drm_mode_getresources(struct drm_device *dev, > void *data, struct drm_file *file_priv); > - > +extern int drm_mode_getplane_res(struct drm_device *dev, void *data, > + struct drm_file *file_priv); > extern int drm_mode_getcrtc(struct drm_device *dev, > void *data, struct drm_file *file_priv); > extern int drm_mode_getconnector(struct drm_device *dev, > void *data, struct drm_file *file_priv); > extern int drm_mode_setcrtc(struct drm_device *dev, > void *data, struct drm_file *file_priv); > +extern int drm_mode_getplane(struct drm_device *dev, > + void *data, struct drm_file *file_priv); > +extern int drm_mode_setplane(struct drm_device *dev, > + void *data, struct drm_file *file_priv); > extern int drm_mode_cursor_ioctl(struct drm_device *dev, > void *data, struct drm_file *file_priv); > extern int drm_mode_addfb(struct drm_device *dev, > diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h > index c4961ea..1e4747c 100644 > --- a/include/drm/drm_mode.h > +++ b/include/drm/drm_mode.h > @@ -120,6 +120,39 @@ struct drm_mode_crtc { > struct drm_mode_modeinfo mode; > }; > > +/* Planes blend with or override other bits on the CRTC */ > +struct drm_mode_set_plane { > + __u32 plane_id; > + __u32 crtc_id; > + __u32 fb_id; /* fb object contains surface format type */ > + > + /* Signed dest location allows it to be partially off screen */ > + __s32 crtc_x, crtc_y; > + __u32 crtc_w, crtc_h; > + > + /* Source values are 16.16 fixed point */ > + __u32 src_x, src_y; > + __u32 src_h, src_w; > +}; > + > +struct drm_mode_get_plane { > + __u32 plane_id; > + > + __u32 crtc_id; > + __u32 fb_id; > + > + __u32 possible_crtcs; > + __u32 gamma_size; > + > + __u32 count_format_types; > + __u64 format_type_ptr; > +}; > + > +struct drm_mode_get_plane_res { > + __u64 plane_id_ptr; > + __u32 count_planes; > +}; > + > #define DRM_MODE_ENCODER_NONE 0 > #define DRM_MODE_ENCODER_DAC 1 > #define DRM_MODE_ENCODER_TMDS 2 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel