summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
authorDave Airlie <airlied@linux.ie>2007-04-05 18:01:02 +1000
committerDave Airlie <airlied@linux.ie>2007-04-05 18:01:02 +1000
commitb4094864f188a1346cc3b51bcb457beeacefbf82 (patch)
tree9e5aedf5decf684eedc8b6d0c16320ae788aab8e /linux-core
parent7bb112fecadc6fe42e5828b861600691071ccd91 (diff)
checkpoint commit: implement SetCrtc so modes can in theory be set from user
This hooks up the userspace mode set it "seems" to work.
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/drm_crtc.c128
-rw-r--r--linux-core/drm_crtc.h4
-rw-r--r--linux-core/drm_drv.c1
3 files changed, 127 insertions, 6 deletions
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
index 2dbe6de1..8e03dd5f 100644
--- a/linux-core/drm_crtc.c
+++ b/linux-core/drm_crtc.c
@@ -302,10 +302,10 @@ bool drm_set_desired_modes(struct drm_device *dev)
continue;
memset(&crtc->mode, 0, sizeof(crtc->mode));
- if (!crtc->desired_mode.crtc_hdisplay) {
+ if (!crtc->desired_mode->crtc_hdisplay) {
}
- if (!drm_crtc_set_mode(crtc, &crtc->desired_mode,
+ if (!drm_crtc_set_mode(crtc, crtc->desired_mode,
crtc->desired_x, crtc->desired_y))
return false;
}
@@ -556,7 +556,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow)
break;
DRM_DEBUG("Setting desired mode for output %s\n", output->name);
drm_mode_debug_printmodeline(dev, des_mode);
- output->crtc->desired_mode = *des_mode;
+ output->crtc->desired_mode = des_mode;
output->initial_x = 0;
output->initial_y = 0;
use_output = output;
@@ -568,7 +568,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow)
break;
DRM_DEBUG("Setting desired mode for output %s\n", output->name);
drm_mode_debug_printmodeline(dev, des_mode);
- output->crtc->desired_mode = *des_mode;
+ output->crtc->desired_mode = des_mode;
#endif
output->initial_x = 0;
output->initial_y = 0;
@@ -596,6 +596,68 @@ void drm_crtc_config_cleanup(drm_device_t *dev)
}
EXPORT_SYMBOL(drm_crtc_config_cleanup);
+int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set)
+{
+ drm_device_t *dev = crtc->dev;
+ struct drm_crtc **save_crtcs, *new_crtc;
+ bool save_enabled = crtc->enabled;
+ bool changed;
+ struct drm_output *output;
+ int count = 0, ro;
+
+ save_crtcs = kzalloc(dev->crtc_config.num_crtc * sizeof(struct drm_crtc *), GFP_KERNEL);
+ if (!save_crtcs)
+ return -ENOMEM;
+
+ if (crtc_info->x != crtc->x || crtc_info->y != crtc->y)
+ changed = true;
+
+ if (crtc->mode.mode_id != new_mode->mode_id)
+ changed = true;
+
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+ save_crtcs[count++] = output->crtc;
+
+ if (output->crtc == crtc)
+ new_crtc = NULL;
+ else
+ new_crtc = output->crtc;
+
+ for (ro = 0; ro < crtc_info->count_outputs; ro++)
+ {
+ if (output_set[ro] == output)
+ new_crtc = crtc;
+ }
+ if (new_crtc != output->crtc) {
+ changed = true;
+ output->crtc = new_crtc;
+ }
+ }
+
+ if (changed) {
+ crtc->enabled = new_mode != NULL;
+ if (new_mode) {
+ DRM_DEBUG("attempting to set mode from userspace\n");
+ drm_mode_debug_printmodeline(dev, new_mode);
+ if (!drm_crtc_set_mode(crtc, new_mode, crtc_info->x,
+ crtc_info->y)) {
+ crtc->enabled = save_enabled;
+ count = 0;
+ list_for_each_entry(output, &dev->crtc_config.output_list, head)
+ output->crtc = save_crtcs[count++];
+ kfree(save_crtcs);
+ return -EINVAL;
+ }
+ crtc->desired_x = crtc_info->x;
+ crtc->desired_y = crtc_info->y;
+ crtc->desired_mode = new_mode;
+ }
+ drm_disable_unused_functions(dev);
+ }
+ kfree(save_crtcs);
+ return 0;
+}
+
void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in)
{
@@ -757,7 +819,6 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp,
drm_device_t *dev = priv->head->dev;
struct drm_mode_get_output __user *argp = (void __user *)arg;
struct drm_mode_get_output out_resp;
- struct drm_crtc *crtc;
struct drm_output *output;
struct drm_display_mode *mode;
int mode_count = 0;
@@ -800,3 +861,60 @@ done:
return retcode;
}
+
+
+int drm_mode_setcrtc(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_crtc __user *argp = (void __user *)arg;
+ struct drm_mode_crtc crtc_req;
+ struct drm_crtc *crtc;
+ struct drm_output **output_set = NULL, *output;
+ struct drm_display_mode *mode;
+ int retcode = 0;
+ int i;
+
+ if (copy_from_user(&crtc_req, argp, sizeof(crtc_req)))
+ return -EFAULT;
+
+ crtc = idr_find(&dev->crtc_config.crtc_idr, crtc_req.crtc_id);
+ if (!crtc || (crtc->id != crtc_req.crtc_id))
+ return -EINVAL;
+
+ if (crtc_req.mode) {
+ mode = idr_find(&dev->crtc_config.crtc_idr, crtc_req.mode);
+ if (!mode || (mode->mode_id != crtc_req.mode))
+ return -EINVAL;
+ } else
+ mode = NULL;
+
+ if (crtc_req.count_outputs == 0 && mode)
+ return -EINVAL;
+
+ if (crtc_req.count_outputs > 0 && !mode)
+ return -EINVAL;
+
+ if (crtc_req.count_outputs > 0) {
+ u32 out_id;
+ output_set = kmalloc(crtc_req.count_outputs * sizeof(struct drm_output *), GFP_KERNEL);
+ if (!output_set)
+ return -ENOMEM;
+
+ for (i = 0; i < crtc_req.count_outputs; i++)
+ {
+ if (get_user(out_id, &crtc_req.set_outputs[i]))
+ return -EFAULT;
+
+ output = idr_find(&dev->crtc_config.crtc_idr, out_id);
+ if (!output || (out_id != output->id))
+ return -EINVAL;
+
+ output_set[i] = output;
+ }
+ }
+
+ retcode = drm_crtc_set_config(crtc, &crtc_req, mode, output_set);
+ return retcode;
+}
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
index e608b462..a2c552e6 100644
--- a/linux-core/drm_crtc.h
+++ b/linux-core/drm_crtc.h
@@ -272,7 +272,7 @@ struct drm_crtc {
struct drm_display_mode mode;
int x, y;
- struct drm_display_mode desired_mode;
+ struct drm_display_mode *desired_mode;
int desired_x, desired_y;
const struct drm_crtc_funcs *funcs;
void *driver_private;
@@ -443,5 +443,7 @@ extern int drm_mode_getcrtc(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
extern int drm_mode_getoutput(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
+extern int drm_mode_setcrtc(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 7d436f8a..c8ee054f 100644
--- a/linux-core/drm_drv.c
+++ b/linux-core/drm_drv.c
@@ -126,6 +126,7 @@ static drm_ioctl_desc_t drm_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_MODE_GETRESOURCES)] = {drm_mode_getresources, DRM_MASTER|DRM_ROOT_ONLY},
[DRM_IOCTL_NR(DRM_IOCTL_MODE_GETCRTC)] = {drm_mode_getcrtc, DRM_MASTER|DRM_ROOT_ONLY},
[DRM_IOCTL_NR(DRM_IOCTL_MODE_GETOUTPUT)] = {drm_mode_getoutput, DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_SETCRTC)] = {drm_mode_setcrtc, DRM_MASTER|DRM_ROOT_ONLY},
};
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )