From 89231953d108e74ee7b0eb99494ead1dd795d640 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 1 May 2007 13:16:29 +1000 Subject: [PATCH] Add support for user defined modes This allows userspace to specify modes and add them to the modesetting system and attach modes to outputs --- libdrm/xf86drmMode.c | 33 +++---- libdrm/xf86drmMode.h | 16 ++-- linux-core/drm_crtc.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++- linux-core/drm_crtc.h | 19 +++- linux-core/drm_drv.c | 4 + shared-core/drm.h | 12 ++- 6 files changed, 312 insertions(+), 29 deletions(-) diff --git a/libdrm/xf86drmMode.c b/libdrm/xf86drmMode.c index b695467..93b0af7 100644 --- a/libdrm/xf86drmMode.c +++ b/libdrm/xf86drmMode.c @@ -376,37 +376,38 @@ err_allocs: return 0; } -#if 0 -uint32_t drmModeNewMode(int fd, struct drm_mode_modeinfo *modeInfo) +uint32_t drmModeAddMode(int fd, struct drm_mode_modeinfo *mode_info) { - /* TODO impl */ + if (ioctl(fd, DRM_IOCTL_MODE_ADDMODE, mode_info)) + return 0; + + return mode_info->id; } -int drmModeDesMode(int fd, uint32_t modeId) +int drmModeRmMode(int fd, uint32_t mode_id) { - // return ioctl(fd, DRM_IOCTL_MODE_DESMODE, modeId); + return ioctl(fd, DRM_IOCTL_MODE_RMMODE, mode_id); } -int drmModeAddMode(int fd, uint32_t outputId, uint32_t modeId) +int drmModeAttachMode(int fd, uint32_t output_id, uint32_t mode_id) { - drm_mode_outputmode_t res; + struct drm_mode_mode_cmd res; - res.outputId = outputId; - res.modeId = modeId; + res.output_id = output_id; + res.mode_id = mode_id; - // return ioctl(fd, DRM_IOCTL_MODE_ADDMODE, &res); + return ioctl(fd, DRM_IOCTL_MODE_ATTACHMODE, &res); } -int drmModeDelMode(int fd, uint32_t outputId, uint32_t modeId) +int drmModeDetachMode(int fd, uint32_t output_id, uint32_t mode_id) { - drm_mode_outputmode_t res; + struct drm_mode_mode_cmd res; - res.outputId = outputId; - res.modeId = modeId; + res.output_id = output_id; + res.mode_id = mode_id; - // return ioctl(fd, DRM_IOCTL_MODE_DELMODE, &res); + return ioctl(fd, DRM_IOCTL_MODE_DETACHMODE, &res); } -#endif diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h index 6aa104a..60e919a 100644 --- a/libdrm/xf86drmMode.h +++ b/libdrm/xf86drmMode.h @@ -261,24 +261,24 @@ extern drmModeOutputPtr drmModeGetOutput(int fd, uint32_t outputId); /** - * Creates a new mode from the given mode info. + * Adds a new mode from the given mode info. * Name must be unique. */ -extern uint32_t drmModeNewMode(int fd, struct drm_mode_modeinfo *modeInfo); +extern uint32_t drmModeAddMode(int fd, struct drm_mode_modeinfo *modeInfo); /** - * Destroys a mode created with CreateMode, must be unused. + * Removes a mode created with AddMode, must be unused. */ -extern int drmModeDesMode(int fd, uint32_t modeId); +extern int drmModeRmMode(int fd, uint32_t modeId); /** - * Adds the given mode to an output. + * Attaches the given mode to an output. */ -extern int drmModeAddMode(int fd, uint32_t outputId, uint32_t modeId); +extern int drmModeAttachMode(int fd, uint32_t outputId, uint32_t modeId); /** - * Deletes a mode Added with AddOutputMode from the output, + * Detaches a mode from the output * must be unused, by the given mode. */ -extern int drmModeDelMode(int fd, uint32_t outputId, uint32_t modeId); +extern int drmModeDetachMode(int fd, uint32_t outputId, uint32_t modeId); diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 16cf62a..201137d 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -749,6 +749,7 @@ void drm_mode_config_init(drm_device_t *dev) INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.output_list); + INIT_LIST_HEAD(&dev->mode_config.usermode_list); idr_init(&dev->mode_config.crtc_idr); } EXPORT_SYMBOL(drm_mode_config_init); @@ -1090,6 +1091,35 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display out->name[DRM_DISPLAY_MODE_LEN-1] = 0; } +/** + * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode + * @out: drm_display_mode to return to the user + * @in: drm_mode_modeinfo to use + * + * LOCKING: + * None. + * + * Convert a drmo_mode_modeinfo into a drm_display_mode structure to return to + * the caller. + */ +void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modeinfo *in) +{ + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} /** * drm_mode_getresources - get graphics configuration @@ -1143,6 +1173,8 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, list_for_each(lh, &output->modes) mode_count++; } + list_for_each(lh, &dev->mode_config.usermode_list) + mode_count++; if (copy_from_user(&card_res, argp, sizeof(card_res))) return -EFAULT; @@ -1155,6 +1187,8 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, list_for_each(lh, &output->modes) mode_count++; } + list_for_each(lh, &dev->mode_config.usermode_list) + mode_count++; } /* handle this in 4 parts */ @@ -1211,6 +1245,14 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, } } } + /* add in user modes */ + list_for_each_entry(mode, &dev->mode_config.usermode_list, head) { + drm_crtc_convert_to_umode(&u_mode, mode); + if (copy_to_user(&card_res.modes[copied++], &u_mode, sizeof(struct drm_mode_modeinfo))) { + retcode = -EFAULT; + goto done; + } + } } card_res.count_modes = mode_count; @@ -1312,6 +1354,7 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, int mode_count = 0; int retcode = 0; int copied = 0; + int i; if (copy_from_user(&out_resp, argp, sizeof(out_resp))) return -EFAULT; @@ -1323,6 +1366,10 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, list_for_each_entry(mode, &output->modes, head) mode_count++; + + for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) + if (output->user_mode_ids[i] != 0) + mode_count++; strncpy(out_resp.name, output->name, DRM_OUTPUT_NAME_LEN); out_resp.name[DRM_OUTPUT_NAME_LEN-1] = 0; @@ -1344,6 +1391,14 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, goto done; } } + for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) { + if (output->user_mode_ids[i] != 0) + if (put_user(output->user_mode_ids[i], &out_resp.modes[copied++])) { + retcode = -EFAULT; + goto done; + } + } + } out_resp.count_modes = mode_count; @@ -1408,9 +1463,13 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, if (!mode || (mode->mode_id != crtc_req.mode)) { struct drm_output *output; - list_for_each_entry(output, &dev->mode_config.output_list, head) { - list_for_each_entry(mode, &output->modes, head) { - drm_mode_debug_printmodeline(dev, mode); + list_for_each_entry(output, + &dev->mode_config.output_list, + head) { + list_for_each_entry(mode, &output->modes, + head) { + drm_mode_debug_printmodeline(dev, + mode); } } @@ -1650,3 +1709,195 @@ void drm_fb_release(struct file *filp) } } + +/** + * drm_fb_newmode - adds a user defined mode + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Adds a user specified mode to the kernel. + * + * Called by the user via ioctl. + * + * RETURNS: + * writes new mode id into arg. + * Zero on success, errno on failure. + */ +int drm_mode_addmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_modeinfo __user *argp = (void __user *)arg; + struct drm_mode_modeinfo new_mode; + struct drm_display_mode *user_mode; + + if (copy_from_user(&new_mode, argp, sizeof(new_mode))) + return -EFAULT; + + user_mode = drm_mode_create(dev); + if (!user_mode) + return -ENOMEM; + + drm_crtc_convert_umode(user_mode, &new_mode); + user_mode->type |= DRM_MODE_TYPE_USERDEF; + + user_mode->output_count = 0; + + spin_lock(&dev->mode_config.config_lock); + list_add(&user_mode->head, &dev->mode_config.usermode_list); + spin_unlock(&dev->mode_config.config_lock); + + new_mode.id = user_mode->mode_id; + if (copy_to_user(argp, &new_mode, sizeof(new_mode))) + return -EFAULT; + + return 0; +} + +/** + * drm_fb_rmmode - removes a user defined mode + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Remove the user defined mode specified by the user. + * + * Called by the user via ioctl + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_rmmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + uint32_t id = arg; + struct drm_display_mode *mode, *t; + int retcode = -EINVAL; + + mode = idr_find(&dev->mode_config.crtc_idr, id); + if (!mode || (id != mode->mode_id)) + return -EINVAL; + + if (!(mode->type & DRM_MODE_TYPE_USERDEF)) + return -EINVAL; + + if (mode->output_count) + return -EINVAL; + + spin_lock(&dev->mode_config.config_lock); + list_for_each_entry(t, &dev->mode_config.usermode_list, head) { + if (t == mode) { + list_del(&mode->head); + drm_mode_destroy(dev, mode); + retcode = 0; + break; + } + } + spin_unlock(&dev->mode_config.config_lock); + return retcode; +} + +/** + * drm_fb_attachmode - Attach a user mode to an output + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * This attaches a user specified mode to an output. + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_attachmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_mode_cmd __user *argp = (void __user *)arg; + struct drm_mode_mode_cmd mode_cmd; + struct drm_output *output; + struct drm_display_mode *mode; + int i; + + if (copy_from_user(&mode_cmd, argp, sizeof(mode_cmd))) + return -EFAULT; + + mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd.mode_id); + if (!mode || (mode->mode_id != mode_cmd.mode_id)) + return -EINVAL; + + output = idr_find(&dev->mode_config.crtc_idr, mode_cmd.output_id); + if (!output || (output->id != mode_cmd.output_id)) + return -EINVAL; + + for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) { + if (output->user_mode_ids[i] == 0) { + output->user_mode_ids[i] = mode->mode_id; + mode->output_count++; + break; + } + } + + if (i == DRM_OUTPUT_MAX_UMODES) + return -ENOSPC; + + return 0; +} + + +/** + * drm_fb_detachmode - Detach a user specified mode from an output + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_detachmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_mode_cmd __user *argp = (void __user *)arg; + struct drm_mode_mode_cmd mode_cmd; + struct drm_output *output; + struct drm_display_mode *mode; + int i, found = 0; + + if (copy_from_user(&mode_cmd, argp, sizeof(mode_cmd))) + return -EFAULT; + + mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd.mode_id); + if (!mode || (mode->mode_id != mode_cmd.mode_id)) + return -EINVAL; + + output = idr_find(&dev->mode_config.crtc_idr, mode_cmd.output_id); + if (!output || (output->id != mode_cmd.output_id)) + return -EINVAL; + + + for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) { + if (output->user_mode_ids[i] == mode->mode_id) { + output->user_mode_ids[i] = 0; + mode->output_count--; + found = 1; + } + } + + if (!found) + return -EINVAL; + + return 0; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index a15ce0c..12c7eef 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -87,6 +87,7 @@ struct drm_display_mode { struct list_head head; char name[DRM_DISPLAY_MODE_LEN]; int mode_id; + int output_count; enum drm_mode_status status; int type; @@ -382,6 +383,7 @@ struct drm_output_funcs { void (*cleanup)(struct drm_output *output); }; +#define DRM_OUTPUT_MAX_UMODES 16 #define DRM_OUTPUT_LEN 32 /** * drm_output - central DRM output control structure @@ -417,6 +419,7 @@ struct drm_output { bool doublescan_allowed; spinlock_t modes_lock; /* protects modes and probed_modes lists */ struct list_head modes; /* list of modes on this output */ + /* OptionInfoPtr options; XF86ConfMonitorPtr conf_monitor; @@ -434,7 +437,8 @@ struct drm_output { char name[DRM_OUTPUT_LEN]; const struct drm_output_funcs *funcs; void *driver_private; - /* RROutputPtr randr_output? */ + + u32 user_mode_ids[DRM_OUTPUT_MAX_UMODES]; }; /** @@ -467,6 +471,7 @@ struct drm_mode_config { int num_crtc; struct list_head crtc_list; + struct list_head usermode_list; int min_width, min_height; int max_width, max_height; /* DamagePtr rotationDamage? */ @@ -480,6 +485,7 @@ struct drm_output *drm_output_create(struct drm_device *dev, const char *name); extern void drm_output_destroy(struct drm_output *output); extern bool drm_output_rename(struct drm_output *output, const char *name); +extern void drm_fb_release(struct file *filp); extern int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter); @@ -507,6 +513,8 @@ extern void drm_mode_sort(struct list_head *mode_list); extern int drm_mode_vrefresh(struct drm_display_mode *mode); extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); +extern void drm_mode_output_list_update(struct drm_output *output); + extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); extern bool drm_initial_config(struct drm_device *dev, bool cangrow); extern void drm_framebuffer_set_object(struct drm_device *dev, @@ -531,5 +539,14 @@ extern int drm_mode_rmfb(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_mode_getfb(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +extern int drm_mode_addmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_rmmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_attachmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_detachmode(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 5aa7137..dc52f30 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -130,6 +130,10 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB)] = {drm_mode_addfb, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_RMFB)] = {drm_mode_rmfb, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETFB)] = {drm_mode_getfb, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDMODE)] = {drm_mode_addmode, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_RMMODE)] = {drm_mode_rmmode, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_ATTACHMODE)] = {drm_mode_attachmode, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_DETACHMODE)] = {drm_mode_detachmode, DRM_MASTER|DRM_ROOT_ONLY}, }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/shared-core/drm.h b/shared-core/drm.h index d42bf73..890dcf8 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -978,6 +978,11 @@ struct drm_mode_fb_cmd { unsigned int depth; }; +struct drm_mode_mode_cmd { + unsigned int output_id; + unsigned int mode_id; +}; + /** * \name Ioctls Definitions */ @@ -1055,7 +1060,12 @@ struct drm_mode_fb_cmd { #define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA3, struct drm_mode_crtc) #define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xA4, struct drm_mode_fb_cmd) #define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xA5, unsigned int) -#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xA6, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xA6, struct drm_mode_fb_cmd) + +#define DRM_IOCTL_MODE_ADDMODE DRM_IOWR(0xA7, struct drm_mode_modeinfo) +#define DRM_IOCTL_MODE_RMMODE DRM_IOWR(0xA8, unsigned int) +#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) +#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xAA, struct drm_mode_mode_cmd) /*@}*/ /** -- 2.7.4