diff options
Diffstat (limited to 'linux-core')
-rw-r--r-- | linux-core/drmP.h | 4 | ||||
-rw-r--r-- | linux-core/drm_crtc.c | 562 | ||||
-rw-r--r-- | linux-core/drm_crtc.h | 30 | ||||
-rw-r--r-- | linux-core/drm_drv.c | 4 | ||||
-rw-r--r-- | linux-core/drm_fb.c | 273 | ||||
-rw-r--r-- | linux-core/drm_modes.c | 2 | ||||
-rw-r--r-- | linux-core/drm_objects.h | 1 | ||||
-rw-r--r-- | linux-core/intel_display.c | 23 | ||||
-rw-r--r-- | linux-core/intel_drv.h | 2 | ||||
-rw-r--r-- | linux-core/intel_fb.c | 274 | ||||
-rw-r--r-- | linux-core/intel_lvds.c | 12 | ||||
-rw-r--r-- | linux-core/intel_sdvo.c | 17 |
12 files changed, 975 insertions, 229 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 2417181d..380570bc 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -662,8 +662,8 @@ struct drm_driver { void (*set_version) (struct drm_device * dev, drm_set_version_t * sv); /* FB routines, if present */ - int (*fb_probe)(struct drm_device *dev, struct drm_framebuffer *fb); - int (*fb_remove)(struct drm_device *dev, struct drm_framebuffer *fb); + int (*fb_probe)(struct drm_device *dev, struct drm_crtc *crtc); + int (*fb_remove)(struct drm_device *dev, struct drm_crtc *crtc); struct drm_fence_driver *fence_driver; struct drm_bo_driver *bo_driver; diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 26a1cf2f..ab8b4688 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -87,6 +87,31 @@ void drm_idr_put(struct drm_device *dev, int id) } /** + * drm_crtc_from_fb - find the CRTC structure associated with an fb + * @dev: DRM device + * @fb: framebuffer in question + * + * LOCKING: + * Caller must hold mode_config lock. + * + * Find CRTC in the mode_config structure that matches @fb. + * + * RETURNS: + * Pointer to the CRTC or NULL if it wasn't found. + */ +struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev, + struct drm_framebuffer *fb) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) + return crtc; + } + return NULL; +} + +/** * drm_framebuffer_create - create a new framebuffer object * @dev: DRM device * @@ -252,7 +277,7 @@ EXPORT_SYMBOL(drm_crtc_in_use); * Detailed mode info for a standard 640x480@60Hz monitor */ static struct drm_display_mode std_mode[] = { - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, + { DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */ }; @@ -465,51 +490,7 @@ done: return ret; } - -/** - * drm_set_desired_modes - set a good mode on every CRTC & output - * @dev: DRM device - * - * LOCKING: - * Caller? (FIXME) - * - * Each CRTC may have a desired mode associated with it. This routine simply - * walks @dev's mode_config and sets the desired mode on every CRTC. Intended - * for use at startup time. - * - * RETURNS: - * True if modes were set, false otherwise. - */ -bool drm_set_desired_modes(struct drm_device *dev) -{ - struct drm_crtc *crtc; - struct drm_output *output, *list_output; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - output = NULL; - - list_for_each_entry(list_output, &dev->mode_config.output_list, - head) { - if (list_output->crtc == crtc) { - output = list_output; - break; - } - } - /* Skip disabled crtcs */ - if (!output) { - DRM_DEBUG("skipping disabled crtc\n"); - continue; - } - - if (!drm_crtc_set_mode(crtc, crtc->desired_mode, - crtc->desired_x, crtc->desired_y)) - return false; - } - - drm_disable_unused_functions(dev); - return true; -} -EXPORT_SYMBOL(drm_set_desired_modes); +EXPORT_SYMBOL(drm_crtc_set_mode); /** * drm_disable_unused_functions - disable unused objects @@ -715,6 +696,7 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev) nmode->mode_id = drm_idr_get(dev, nmode); return nmode; } +EXPORT_SYMBOL(drm_mode_create); /** * drm_mode_destroy - remove a mode @@ -732,6 +714,7 @@ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) kfree(mode); } +EXPORT_SYMBOL(drm_mode_destroy); /** * drm_mode_config_init - initialize DRM mode_configuration structure @@ -749,6 +732,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); @@ -798,26 +782,94 @@ out_err: } /** - * drm_setup_output - setup an output structure - * @output: output to setup - * @crtc: CRTC this output belongs to - * @mode: desired mode for this output + * drm_pick_crtcs - pick crtcs for output devices + * @dev: DRM device * * LOCKING: * None. - * - * Setup @output with the parameters given, with its initial coordinates set - * at the origin. */ -static void drm_setup_output(struct drm_output *output, struct drm_crtc *crtc, - struct drm_display_mode *mode) +static void drm_pick_crtcs (drm_device_t *dev) { - output->crtc = crtc; - output->crtc->desired_mode = mode; - output->initial_x = 0; - output->initial_y = 0; + int c, o; + struct drm_output *output, *output_equal; + struct drm_crtc *crtc; + struct drm_display_mode *des_mode = NULL, *modes, *modes_equal; + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + output->crtc = NULL; + + /* Don't hook up outputs that are disconnected ?? + * + * This is debateable. Do we want fixed /dev/fbX or + * dynamic on hotplug (need mode code for that though) ? + * + * If we don't hook up outputs now, then we only create + * /dev/fbX for the output that's enabled, that's good as + * the users console will be on that output. + * + * If we do hook up outputs that are disconnected now, then + * the user may end up having to muck about with the fbcon + * map flags to assign his console to the enabled output. Ugh. + */ + if (output->status != output_status_connected) + continue; + + des_mode = NULL; + list_for_each_entry(des_mode, &output->modes, head) { + if (des_mode->flags & DRM_MODE_TYPE_PREFERRED) + break; + } + + + /* No preferred mode, let's select another which should pick + * the default 640x480 if nothing else is here. + * + */ + if (!des_mode || !(des_mode->flags & DRM_MODE_TYPE_PREFERRED)) { + list_for_each_entry(des_mode, &output->modes, head) { + if (des_mode->flags & DRM_MODE_TYPE_DEFAULT) + break; + } + } + + c = -1; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + c++; + if ((output->possible_crtcs & (1 << c)) == 0) + continue; + +#if 0 /* should we try and clone ?? - code not tested - FIXME */ + o = -1; + list_for_each_entry(output_equal, &dev->mode_config.output_list, head) { + o++; + if (output->id == output_equal->id) + continue; + + list_for_each_entry(modes, &output->modes, head) { + list_for_each_entry(modes_equal, &output_equal->modes, head) { + if (drm_mode_equal (modes, modes_equal)) { + if ((output->possible_clones & (1 << o))) { + goto clone; + } + } + } + } + } + +clone: +#endif + /* Found a CRTC to attach to, do it ! */ + output->crtc = crtc; + output->crtc->desired_mode = des_mode; + output->initial_x = 0; + output->initial_y = 0; + DRM_DEBUG("Desired mode for CRTC %d is %s\n",c,des_mode->name); + break; + } + } } + /** * drm_initial_config - setup a sane initial output configuration * @dev: DRM device @@ -830,108 +882,68 @@ static void drm_setup_output(struct drm_output *output, struct drm_crtc *crtc, * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * - * FIXME: return value and better initial config. - * * RETURNS: * Zero if everything went ok, nonzero otherwise. */ bool drm_initial_config(drm_device_t *dev, bool can_grow) { /* do a hardcoded initial configuration here */ - struct drm_crtc *crtc, *vga_crtc = NULL, *tmds_crtc = NULL, - *lvds_crtc = NULL; + struct drm_display_mode *des_mode = NULL; struct drm_output *output; struct drm_framebuffer *fb; drm_buffer_object_t *fbo; unsigned long size, bytes_per_pixel; - - fb = drm_framebuffer_create(dev); - if (!fb) { - DRM_ERROR("failed to allocate fb.\n"); - return true; - } - - /* bind both CRTCs to this fb */ - /* only initialise one crtc to enabled state */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - crtc->fb = fb; - if (!vga_crtc) { - vga_crtc = crtc; - crtc->enabled = 1; - crtc->desired_x = 0; - crtc->desired_y = 0; - } else { - if (!lvds_crtc) { - lvds_crtc = crtc; - crtc->enabled = 1; - crtc->desired_x = 0; - crtc->desired_y = 0; - } - if (!tmds_crtc) { - tmds_crtc = crtc; - crtc->enabled = 1; - crtc->desired_x = 0; - crtc->desired_y = 0; - } - } - } + int ret; drm_crtc_probe_output_modes(dev, 2048, 2048); - /* hard bind the CRTCS */ + drm_pick_crtcs(dev); - /* bind analog output to one crtc */ list_for_each_entry(output, &dev->mode_config.output_list, head) { - struct drm_display_mode *des_mode = NULL; - if (list_empty(&output->modes)) + /* can't setup the output if there's no assigned crtc or mode */ + if (!output->crtc || !output->crtc->desired_mode) continue; - /* Get the first preferred moded */ - list_for_each_entry(des_mode, &output->modes, head) { - if (des_mode->flags & DRM_MODE_TYPE_PREFERRED) - break; + fb = drm_framebuffer_create(dev); + if (!fb) { + DRM_ERROR("failed to allocate fb.\n"); + return true; } + output->crtc->fb = fb; + des_mode = output->crtc->desired_mode; - if (!des_mode) - continue; - - if (!strncmp(output->name, "VGA", 3)) { - DRM_DEBUG("VGA preferred mode: %s\n", des_mode->name); - drm_setup_output(output, vga_crtc, des_mode); - } else if (!strncmp(output->name, "TMDS", 4)) { - DRM_DEBUG("TMDS preferred mode: %s\n", des_mode->name); - drm_setup_output(output, tmds_crtc, des_mode); - } else if (!strncmp(output->name, "LVDS", 3)) { - DRM_DEBUG("LVDS preferred mode: %s\n", des_mode->name); - drm_setup_output(output, lvds_crtc, des_mode); - } else - output->crtc = NULL; - - /* FB config is max of above desired resolutions */ - /* FIXME: per-output FBs/CRTCs */ if (des_mode->hdisplay > fb->width) fb->width = des_mode->hdisplay; if (des_mode->vdisplay > fb->height) fb->height = des_mode->vdisplay; - } - /* FIXME: multiple depths */ - bytes_per_pixel = 4; - fb->bits_per_pixel = bytes_per_pixel * 8; - fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8); - fb->depth = bytes_per_pixel * 8; - size = fb->width * fb->height * bytes_per_pixel; - drm_buffer_object_create(dev, size, drm_bo_type_kernel, - DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | - DRM_BO_FLAG_MEM_PRIV0 | DRM_BO_FLAG_NO_MOVE, - 0, 0, 0, - &fbo); - DRM_DEBUG("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width, - fb->height, fbo->offset, fbo); - fb->offset = fbo->offset; - fb->bo = fbo; - dev->driver->fb_probe(dev, fb); + /* FIXME: multiple depths */ + bytes_per_pixel = 4; + fb->bits_per_pixel = 32; + fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8); + fb->depth = 24; + size = fb->width * fb->height * bytes_per_pixel; + /* FIXME - what about resizeable objects ??? */ + ret = drm_buffer_object_create(dev, size, drm_bo_type_kernel, + DRM_BO_FLAG_READ | + DRM_BO_FLAG_WRITE | + DRM_BO_FLAG_MEM_PRIV0 | + DRM_BO_FLAG_NO_MOVE, + 0, 0, 0, + &fbo); + if (ret) { + printk(KERN_ERR "failed to allocate framebuffer\n"); + drm_framebuffer_destroy(fb); + continue; + } + fb->offset = fbo->offset; + fb->bo = fbo; + printk("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width, + fb->height, fbo->offset, fbo); + dev->driver->fb_probe(dev, output->crtc); + } + drm_disable_unused_functions(dev); return false; } @@ -954,6 +966,7 @@ void drm_mode_config_cleanup(drm_device_t *dev) struct drm_output *output, *ot; struct drm_crtc *crtc, *ct; struct drm_framebuffer *fb, *fbt; + struct drm_display_mode *mode, *mt; list_for_each_entry_safe(output, ot, &dev->mode_config.output_list, head) { drm_output_destroy(output); } @@ -962,8 +975,12 @@ void drm_mode_config_cleanup(drm_device_t *dev) drm_crtc_destroy(crtc); } + list_for_each_entry_safe(mode, mt, &dev->mode_config.usermode_list, head) { + drm_mode_destroy(dev, mode); + } + list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { - dev->driver->fb_remove(dev, fb); + dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb)); /* If this FB was the kernel one, free it */ if (fb->bo->type == drm_bo_type_kernel) { mutex_lock(&dev->struct_mutex); @@ -1089,6 +1106,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 @@ -1142,6 +1188,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; @@ -1154,6 +1202,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 */ @@ -1210,6 +1260,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; @@ -1311,6 +1369,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; @@ -1322,6 +1381,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; @@ -1343,6 +1406,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; @@ -1407,9 +1478,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); } } @@ -1517,17 +1592,20 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, r.buffer_id = fb->id; list_add(&fb->filp_head, &priv->fbs); + + if (copy_to_user(argp, &r, sizeof(r))) + return -EFAULT; + /* bind the fb to the crtc for now */ { struct drm_crtc *crtc; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { crtc->fb = fb; + + dev->driver->fb_probe(dev, crtc); } } - if (copy_to_user(argp, &r, sizeof(r))) - return -EFAULT; - dev->driver->fb_probe(dev, fb); return 0; } @@ -1563,7 +1641,8 @@ int drm_mode_rmfb(struct inode *inode, struct file *filp, return -EINVAL; } - dev->driver->fb_remove(dev, fb); + dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb)); + /* TODO check if we own the buffer */ /* TODO release all crtc connected to the framebuffer */ /* bind the fb to the crtc for now */ @@ -1644,8 +1723,199 @@ void drm_fb_release(struct file *filp) list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { list_del(&fb->filp_head); - dev->driver->fb_remove(dev, fb); + dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb)); drm_framebuffer_destroy(fb); - } } + +/** + * 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 a15ce0cf..fa143e6e 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; @@ -293,8 +294,8 @@ struct drm_crtc_funcs { void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y); /* Set gamma on the CRTC */ - void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - int size); + void (*gamma_set)(struct drm_crtc *crtc, u16 r, u16 g, u16 b, + int regno); /* Driver cleanup routine */ void (*cleanup)(struct drm_crtc *crtc); }; @@ -319,7 +320,7 @@ struct drm_crtc { int id; /* idr assigned */ - /* framebuffer the CRTC is currently bound to */ + /* framebuffer the output is currently bound to */ struct drm_framebuffer *fb; bool enabled; @@ -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,9 @@ 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 +472,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 +486,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); @@ -492,6 +499,7 @@ extern void drm_mode_debug_printmodeline(struct drm_device *dev, extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); extern void drm_mode_set_name(struct drm_display_mode *mode); +extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); extern void drm_disable_unused_functions(struct drm_device *dev); extern struct drm_display_mode *drm_mode_create(struct drm_device *dev); @@ -507,12 +515,13 @@ 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, unsigned long handle); -extern bool drm_set_desired_modes(struct drm_device *dev); -extern int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb); +extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); /* IOCTLs */ @@ -531,5 +540,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 5aa7137b..dc52f302 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/linux-core/drm_fb.c b/linux-core/drm_fb.c index 1eb31952..5f2b1cea 100644 --- a/linux-core/drm_fb.c +++ b/linux-core/drm_fb.c @@ -39,9 +39,11 @@ #include <linux/init.h> #include "drmP.h" +#include "drm_crtc.h" + struct drmfb_par { struct drm_device *dev; - struct drm_framebuffer *fb; + struct drm_crtc *crtc; }; static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, @@ -49,11 +51,20 @@ static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, struct fb_info *info) { struct drmfb_par *par = info->par; - struct drm_framebuffer *fb = par->fb; - if (regno > 17) + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_crtc *crtc = par->crtc; + + if (regno > 255) return 1; - if (regno < 16) { + if (fb->depth == 8) { + if (crtc->funcs->gamma_set) { + crtc->funcs->gamma_set(crtc, red, green, blue, regno); + } + return 0; + } + + if (regno < 16) { switch (fb->depth) { case 15: fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | @@ -72,7 +83,118 @@ static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, ((blue & 0xff00) >> 8); break; } + } + + return 0; +} + +static int drmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct drmfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_display_mode *drm_mode; + struct drm_output *output; + int depth; + + if (!var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + depth = var->bits_per_pixel; + break; + } + + switch (depth) { + case 8: + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 6; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + return -EINVAL; + } + +#if 0 + /* Here we walk the output mode list and look for modes. If we haven't + * got it, then bail. Not very nice, so this is disabled. + * In the set_par code, we create our mode based on the incoming + * parameters. Nicer, but may not be desired by some. + */ + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; } + + list_for_each_entry(drm_mode, &output->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres && + drm_mode->clock != 0) + break; + } + + if (!drm_mode) + return -EINVAL; +#endif return 0; } @@ -81,9 +203,74 @@ static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, static int drmfb_set_par(struct fb_info *info) { struct drmfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; struct drm_device *dev = par->dev; + struct drm_display_mode *drm_mode; + struct fb_var_screeninfo *var = &info->var; + struct drm_output *output; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + fb->depth = var->bits_per_pixel; + break; + } + + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + info->screen_size = info->fix.smem_len; /* ??? */ + + /* Should we walk the output's modelist or just create our own ??? + * For now, we create and destroy a mode based on the incoming + * parameters. But there's commented out code below which scans + * the output list too. + */ +#if 0 + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + list_for_each_entry(drm_mode, &output->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres && + drm_mode->clock != 0) + break; + } +#else + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode_set_name(drm_mode); +#endif + + if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) + return -EINVAL; + + /* Have to destroy our created mode if we're not searching the mode + * list for it. + */ +#if 1 + drm_mode_destroy(dev, drm_mode); +#endif - drm_set_desired_modes(dev); return 0; } @@ -94,6 +281,7 @@ static struct fb_ops drmfb_ops = { // .fb_write = drmfb_write, // .fb_release = drmfb_release, // .fb_ioctl = drmfb_ioctl, + .fb_check_var = drmfb_check_var, .fb_set_par = drmfb_set_par, .fb_setcolreg = drmfb_setcolreg, .fb_fillrect = cfb_fillrect, @@ -101,11 +289,13 @@ static struct fb_ops drmfb_ops = { .fb_imageblit = cfb_imageblit, }; -int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) +int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc) { struct fb_info *info; + struct drm_framebuffer *fb = crtc->fb; struct drmfb_par *par; struct device *device = &dev->pdev->dev; + struct drm_display_mode *mode = crtc->desired_mode; int ret; info = framebuffer_alloc(sizeof(struct drmfb_par), device); @@ -118,20 +308,20 @@ int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) par = info->par; par->dev = dev; - par->fb = fb; + par->crtc = crtc; info->fbops = &drmfb_ops; strcpy(info->fix.id, "drmfb"); - info->fix.smem_start = fb->offset + dev->mode_config.fb_base; - info->fix.smem_len = fb->bo->mem.size; info->fix.type = FB_TYPE_PACKED_PIXELS; - info->fix.visual = FB_VISUAL_DIRECTCOLOR; + info->fix.visual = FB_VISUAL_TRUECOLOR; info->fix.accel = FB_ACCEL_NONE; info->fix.type_aux = 0; info->fix.mmio_start = 0; info->fix.mmio_len = 0; - info->fix.line_length = fb->pitch * ((fb->bits_per_pixel + 1) / 8); + info->fix.line_length = fb->pitch; + info->fix.smem_start = fb->offset + dev->mode_config.fb_base; + info->fix.smem_len = info->fix.line_length * fb->height; info->flags = FBINFO_DEFAULT; @@ -140,11 +330,9 @@ int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) DRM_ERROR("error mapping fb: %d\n", ret); info->screen_base = fb->virtual_base; - info->screen_size = fb->bo->mem.size; + info->screen_size = info->fix.smem_len; /* ??? */ info->pseudo_palette = fb->pseudo_palette; - info->var.xres = fb->width; - info->var.xres_virtual = fb->pitch; - info->var.yres = fb->height; + info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; info->var.bits_per_pixel = fb->bits_per_pixel; info->var.xoffset = 0; @@ -154,24 +342,69 @@ int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) info->var.width = -1; info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.xres = mode->hdisplay; + info->var.right_margin = mode->hsync_start - mode->hdisplay; + info->var.hsync_len = mode->hsync_end - mode->hsync_start; + info->var.left_margin = mode->htotal - mode->hsync_end; + info->var.yres = mode->vdisplay; + info->var.lower_margin = mode->vsync_start - mode->vdisplay; + info->var.vsync_len = mode->vsync_end - mode->vsync_start; + info->var.upper_margin = mode->vtotal - mode->vsync_end; + info->var.pixclock = 10000000 / mode->htotal * 1000 / + mode->vtotal * 100; + /* avoid overflow */ + info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + DRM_DEBUG("fb depth is %d\n", fb->depth); switch(fb->depth) { case 8: + info->var.red.offset = 0; + info->var.green.offset = 0; + info->var.blue.offset = 0; + info->var.red.length = 8; /* 8bit DAC */ + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + info->var.transp.length = 0; break; - default: case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; case 32: info->var.red.offset = 16; info->var.green.offset = 8; info->var.blue.offset = 0; info->var.red.length = info->var.green.length = info->var.blue.length = 8; - if (fb->depth == 32) { - info->var.transp.offset = 24; - info->var.transp.length = 8; - } + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: break; } diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index 3293f91d..97f7607d 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -533,7 +533,7 @@ void drm_mode_sort(struct list_head *mode_list) */ void drm_mode_output_list_update(struct drm_output *output) { - struct drm_display_mode *mode, *t; + struct drm_display_mode *mode; struct drm_display_mode *pmode, *pt; int found_it; list_for_each_entry_safe(pmode, pt, &output->probed_modes, diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h index 8a70c734..0c7f2e32 100644 --- a/linux-core/drm_objects.h +++ b/linux-core/drm_objects.h @@ -459,6 +459,7 @@ extern int drm_bo_mem_space(drm_buffer_object_t * bo, drm_bo_mem_reg_t * mem, int no_wait); extern int drm_bo_move_buffer(drm_buffer_object_t * bo, uint32_t new_mem_flags, int no_wait, int move_unfenced); +extern int drm_bo_clean_mm(struct drm_device *dev, unsigned mem_type); /* * Buffer object memory move helpers. diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index be2db912..3fce0941 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -787,6 +787,9 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, else dpll |= PLL_REF_INPUT_DREFCLK; + /* setup pipeconf */ + pipeconf = I915_READ(pipeconf_reg); + /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -814,7 +817,6 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, else dspcntr |= DISPPLANE_SEL_PIPE_B; - pipeconf = I915_READ(pipeconf_reg); if (pipe == 0 && !IS_I965G(dev)) { /* Enable pixel doubling when the dot clock is > 90% of the (display) * core speed. @@ -955,19 +957,14 @@ void intel_crtc_load_lut(struct drm_crtc *crtc) } /** Sets the color ramps on behalf of RandR */ -static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int size) +static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) { struct intel_crtc *intel_crtc = crtc->driver_private; - int i; - for (i = 0; i < 256; i++) { - intel_crtc->lut_r[i] = red[i] >> 8; - intel_crtc->lut_g[i] = green[i] >> 8; - intel_crtc->lut_b[i] = blue[i] >> 8; - } - - intel_crtc_load_lut(crtc); + intel_crtc->lut_r[regno] = red >> 8; + intel_crtc->lut_g[regno] = green >> 8; + intel_crtc->lut_b[regno] = blue >> 8; } /* Returns the clock of the currently programmed mode of the given pipe. */ @@ -1176,7 +1173,8 @@ static void intel_setup_outputs(drm_device_t *dev) (1 << INTEL_OUTPUT_SDVO)); break; case INTEL_OUTPUT_ANALOG: - crtc_mask = ((1 << 0)); + crtc_mask = ((1 << 0)| + (1 << 1)); clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | (1 << INTEL_OUTPUT_DVO) | (1 << INTEL_OUTPUT_SDVO)); @@ -1223,7 +1221,6 @@ void intel_modeset_init(drm_device_t *dev) intel_setup_outputs(dev); //drm_initial_config(dev, false); - //drm_set_desired_modes(dev); } void intel_modeset_cleanup(drm_device_t *dev) diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h index 91112ff5..9205b99b 100644 --- a/linux-core/intel_drv.h +++ b/linux-core/intel_drv.h @@ -72,7 +72,7 @@ extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_output_prepare (struct drm_output *output); extern void intel_output_commit (struct drm_output *output); extern struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev, - struct drm_crtc *crtc); + struct drm_crtc *crtc); extern void intel_wait_for_vblank(drm_device_t *dev); extern struct drm_crtc *intel_get_crtc_from_pipe(drm_device_t *dev, int pipe); diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c index 267b4fd6..7126c16c 100644 --- a/linux-core/intel_fb.c +++ b/linux-core/intel_fb.c @@ -40,12 +40,13 @@ #include "drmP.h" #include "drm.h" +#include "drm_crtc.h" #include "i915_drm.h" #include "i915_drv.h" struct intelfb_par { struct drm_device *dev; - struct drm_framebuffer *fb; + struct drm_crtc *crtc; }; static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, @@ -53,10 +54,18 @@ static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, struct fb_info *info) { struct intelfb_par *par = info->par; - struct drm_framebuffer *fb = par->fb; - if (regno > 17) + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_crtc *crtc = par->crtc; + + if (regno > 255) return 1; + if (fb->depth == 8) { + if (crtc->funcs->gamma_set) + crtc->funcs->gamma_set(crtc, red, green, blue, regno); + return 0; + } + if (regno < 16) { switch (fb->depth) { case 15: @@ -81,16 +90,194 @@ static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, return 0; } +static int intelfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_display_mode *drm_mode; + struct drm_output *output; + int depth; + + if (!var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + depth = var->bits_per_pixel; + break; + } + + switch (depth) { + case 8: + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 6; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + return -EINVAL; + } + +#if 0 + /* Here we walk the output mode list and look for modes. If we haven't + * got it, then bail. Not very nice, so this is disabled. + * In the set_par code, we create our mode based on the incoming + * parameters. Nicer, but may not be desired by some. + */ + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + list_for_each_entry(drm_mode, &output->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres && + drm_mode->clock != 0) + break; + } + + if (!drm_mode) + return -EINVAL; +#endif + + return 0; +} + /* this will let fbcon do the mode init */ static int intelfb_set_par(struct fb_info *info) { struct intelfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; struct drm_device *dev = par->dev; + struct drm_display_mode *drm_mode; + struct fb_var_screeninfo *var = &info->var; + struct drm_output *output; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + fb->depth = var->bits_per_pixel; + break; + } + + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + info->screen_size = info->fix.smem_len; /* ??? */ + + /* Should we walk the output's modelist or just create our own ??? + * For now, we create and destroy a mode based on the incoming + * parameters. But there's commented out code below which scans + * the output list too. + */ +#if 0 + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + list_for_each_entry(drm_mode, &output->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres && + drm_mode->clock != 0) + break; + } +#else + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode_set_name(drm_mode); +#endif + + if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) + return -EINVAL; + + /* Have to destroy our created mode if we're not searching the mode + * list for it. + */ +#if 1 + drm_mode_destroy(dev, drm_mode); +#endif - drm_set_desired_modes(dev); return 0; } +#if 0 static void intelfb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { @@ -234,6 +421,7 @@ void intelfb_imageblit(struct fb_info *info, const struct fb_image *image) OUT_RING(MI_NOOP); ADVANCE_LP_RING(); } +#endif static struct fb_ops intelfb_ops = { .owner = THIS_MODULE, @@ -242,18 +430,21 @@ static struct fb_ops intelfb_ops = { // .fb_write = intelfb_write, // .fb_release = intelfb_release, // .fb_ioctl = intelfb_ioctl, + .fb_check_var = intelfb_check_var, .fb_set_par = intelfb_set_par, .fb_setcolreg = intelfb_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, //intelfb_copyarea, - .fb_imageblit = intelfb_imageblit, + .fb_imageblit = cfb_imageblit, //intelfb_imageblit, }; -int intelfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) +int intelfb_probe(struct drm_device *dev, struct drm_crtc *crtc) { struct fb_info *info; struct intelfb_par *par; struct device *device = &dev->pdev->dev; + struct drm_framebuffer *fb = crtc->fb; + struct drm_display_mode *mode = crtc->desired_mode; int ret; info = framebuffer_alloc(sizeof(struct intelfb_par), device); @@ -266,24 +457,24 @@ int intelfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) par = info->par; par->dev = dev; - par->fb = fb; + par->crtc = crtc; info->fbops = &intelfb_ops; strcpy(info->fix.id, "intelfb"); - info->fix.smem_start = fb->offset + dev->mode_config.fb_base; - info->fix.smem_len = fb->bo->mem.size; info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; info->fix.type_aux = 0; info->fix.xpanstep = 8; info->fix.ypanstep = 1; info->fix.ywrapstep = 0; - info->fix.visual = FB_VISUAL_DIRECTCOLOR; info->fix.accel = FB_ACCEL_I830; info->fix.type_aux = 0; info->fix.mmio_start = 0; info->fix.mmio_len = 0; info->fix.line_length = fb->pitch; + info->fix.smem_start = fb->offset + dev->mode_config.fb_base; + info->fix.smem_len = info->fix.line_length * fb->height; info->flags = FBINFO_DEFAULT; @@ -292,11 +483,9 @@ int intelfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) DRM_ERROR("error mapping fb: %d\n", ret); info->screen_base = fb->virtual_base; - info->screen_size = fb->bo->mem.size; + info->screen_size = info->fix.smem_len; /* FIXME */ info->pseudo_palette = fb->pseudo_palette; - info->var.xres = fb->width; info->var.xres_virtual = fb->width; - info->var.yres = fb->height; info->var.yres_virtual = fb->height; info->var.bits_per_pixel = fb->bits_per_pixel; info->var.xoffset = 0; @@ -306,6 +495,17 @@ int intelfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) info->var.width = -1; info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.xres = mode->hdisplay; + info->var.right_margin = mode->hsync_start - mode->hdisplay; + info->var.hsync_len = mode->hsync_end - mode->hsync_start; + info->var.left_margin = mode->htotal - mode->hsync_end; + info->var.yres = mode->vdisplay; + info->var.lower_margin = mode->vsync_start - mode->vdisplay; + info->var.vsync_len = mode->vsync_end - mode->vsync_start; + info->var.upper_margin = mode->vtotal - mode->vsync_end; + info->var.pixclock = 10000000 / mode->htotal * 1000 / + mode->vtotal * 100000 / mode->vrefresh; + info->pixmap.size = 64*1024; info->pixmap.buf_align = 8; info->pixmap.access_align = 32; @@ -316,21 +516,52 @@ int intelfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) DRM_DEBUG(" pitch is %d\n", fb->pitch); switch(fb->depth) { case 8: - case 15: + info->var.red.offset = 0; + info->var.green.offset = 0; + info->var.blue.offset = 0; + info->var.red.length = 8; /* 8bit DAC */ + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; case 16: - break; - default: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + break; case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; case 32: info->var.red.offset = 16; info->var.green.offset = 8; info->var.blue.offset = 0; info->var.red.length = info->var.green.length = info->var.blue.length = 8; - if (fb->depth == 32) { - info->var.transp.offset = 24; - info->var.transp.length = 8; - } + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: break; } @@ -343,8 +574,9 @@ int intelfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) } EXPORT_SYMBOL(intelfb_probe); -int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +int intelfb_remove(struct drm_device *dev, struct drm_crtc *crtc) { + struct drm_framebuffer *fb = crtc->fb; struct fb_info *info = fb->fbdev; if (info) { diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index 74b040ba..942eb2ab 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -164,6 +164,13 @@ static bool intel_lvds_mode_fixup(struct drm_output *output, struct intel_crtc *intel_crtc = output->crtc->driver_private; struct drm_output *tmp_output; + /* Should never happen!! */ + if (!IS_I965G(dev) && intel_crtc->pipe == 0) { + printk(KERN_ERR "Can't support LVDS on pipe A\n"); + return false; + } + + /* Should never happen!! */ list_for_each_entry(tmp_output, &dev->mode_config.output_list, head) { if (tmp_output != output && tmp_output->crtc == output->crtc) { printk(KERN_ERR "Can't enable LVDS and another " @@ -172,11 +179,6 @@ static bool intel_lvds_mode_fixup(struct drm_output *output, } } - if (intel_crtc->pipe == 0) { - printk(KERN_ERR "Can't support LVDS on pipe A\n"); - return false; - } - /* * If we have timings from the BIOS for the panel, put them in * to the adjusted mode. The CRTC will be set up for this mode, diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c index 98c4034d..c02fd958 100644 --- a/linux-core/intel_sdvo.c +++ b/linux-core/intel_sdvo.c @@ -525,7 +525,6 @@ static bool intel_sdvo_get_preferred_input_timing(struct drm_output *output, static int intel_sdvo_get_clock_rate_mult(struct drm_output *output) { - struct intel_output *intel_output = output->driver_private; u8 response, status; intel_sdvo_write_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0); @@ -895,8 +894,6 @@ static enum drm_output_status intel_sdvo_detect(struct drm_output *output) static int intel_sdvo_get_modes(struct drm_output *output) { - struct drm_display_mode *modes; - /* set the bus switch and get the modes */ intel_sdvo_set_control_bus_switch(output, SDVO_CONTROL_BUS_DDC2); intel_ddc_get_modes(output); @@ -1013,26 +1010,18 @@ void intel_sdvo_init(drm_device_t *dev, int output_device) memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs)); - /* TODO, CVBS, SVID, YPRPB & SCART outputs. - * drm_initial_config probably wants tweaking too to support the - * above. But has fixed VGA, TMDS and LVDS checking code. That should - * be dealt with. - */ + /* TODO, CVBS, SVID, YPRPB & SCART outputs. */ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) { sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0; output->subpixel_order = SubPixelHorizontalRGB; - /* drm_initial_config wants this name, but should be RGB */ - /* Use this for now.... */ - name_prefix="VGA"; + name_prefix="RGB"; } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) { sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; output->subpixel_order = SubPixelHorizontalRGB; - /* drm_initial_config wants this name, but should be RGB */ - /* Use this for now.... */ - name_prefix="VGA"; + name_prefix="RGB"; } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) { |