diff options
author | Dave Airlie <airlied@redhat.com> | 2009-12-08 07:03:55 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-12-08 07:03:55 +1000 |
commit | f84676185368e36c6bc0eeab87ab73ed39042648 (patch) | |
tree | 7d97885644c7b6a09c244de11af94409da665e62 /drivers/gpu | |
parent | 22dd50133ab7548adb23e86c302d6e8b75817e8c (diff) | |
parent | 447aeb907e417e0e837b4a4026d5081c88b6e8ca (diff) |
Merge remote branch 'origin/drm-core-next' into test
Conflicts:
drivers/gpu/drm/drm_fb_helper.c
Diffstat (limited to 'drivers/gpu')
25 files changed, 1648 insertions, 371 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 3c8827a7aab..91567ac806f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -15,7 +15,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o -drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o +drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5cae0b3eee9..4fe321dc900 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -125,6 +125,15 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, drm_tv_subconnector_enum_list) +static struct drm_prop_enum_list drm_dirty_info_enum_list[] = { + { DRM_MODE_DIRTY_OFF, "Off" }, + { DRM_MODE_DIRTY_ON, "On" }, + { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, +}; + +DRM_ENUM_NAME_FN(drm_get_dirty_info_name, + drm_dirty_info_enum_list) + struct drm_conn_prop_enum_list { int type; char *name; @@ -802,6 +811,36 @@ int drm_mode_create_dithering_property(struct drm_device *dev) EXPORT_SYMBOL(drm_mode_create_dithering_property); /** + * drm_mode_create_dirty_property - create dirty property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired + * connectors. + */ +int drm_mode_create_dirty_info_property(struct drm_device *dev) +{ + struct drm_property *dirty_info; + int i; + + if (dev->mode_config.dirty_info_property) + return 0; + + dirty_info = + drm_property_create(dev, DRM_MODE_PROP_ENUM | + DRM_MODE_PROP_IMMUTABLE, + "dirty", + ARRAY_SIZE(drm_dirty_info_enum_list)); + for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++) + drm_property_add_enum(dirty_info, i, + drm_dirty_info_enum_list[i].type, + drm_dirty_info_enum_list[i].name); + dev->mode_config.dirty_info_property = dirty_info; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_dirty_info_property); + +/** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * @@ -1753,6 +1792,71 @@ out: return ret; } +int drm_mode_dirtyfb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_clip_rect __user *clips_ptr; + struct drm_clip_rect *clips = NULL; + struct drm_mode_fb_dirty_cmd *r = data; + struct drm_mode_object *obj; + struct drm_framebuffer *fb; + unsigned flags; + int num_clips; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_ERROR("invalid framebuffer id\n"); + ret = -EINVAL; + goto out_err1; + } + fb = obj_to_fb(obj); + + num_clips = r->num_clips; + clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr; + + if (!num_clips != !clips_ptr) { + ret = -EINVAL; + goto out_err1; + } + + flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; + + /* If userspace annotates copy, clips must come in pairs */ + if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { + ret = -EINVAL; + goto out_err1; + } + + if (num_clips && clips_ptr) { + clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); + if (!clips) { + ret = -ENOMEM; + goto out_err1; + } + + ret = copy_from_user(clips, clips_ptr, + num_clips * sizeof(*clips)); + if (ret) + goto out_err2; + } + + if (fb->funcs->dirty) { + ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips); + } else { + ret = -ENOSYS; + goto out_err2; + } + +out_err2: + kfree(clips); +out_err1: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + + /** * drm_fb_release - remove and free the FBs on this file * @filp: file * from the ioctl @@ -2478,3 +2582,72 @@ out: mutex_unlock(&dev->mode_config.mutex); return ret; } + +int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_page_flip *page_flip = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + struct drm_pending_vblank_event *e = NULL; + unsigned long flags; + int ret = -EINVAL; + + if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || + page_flip->reserved != 0) + return -EINVAL; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) + goto out; + crtc = obj_to_crtc(obj); + + if (crtc->funcs->page_flip == NULL) + goto out; + + obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) + goto out; + fb = obj_to_fb(obj); + + if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { + ret = -ENOMEM; + spin_lock_irqsave(&dev->event_lock, flags); + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + file_priv->event_space -= sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (e == NULL) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + + e->event.base.type = DRM_EVENT_VBLANK; + e->event.base.length = sizeof e->event; + e->event.user_data = page_flip->user_data; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = + (void (*) (struct drm_pending_event *)) kfree; + } + + ret = crtc->funcs->page_flip(crtc, fb, e); + if (ret) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); + } + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index bbfd110a716..3963b3c1081 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -109,7 +109,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, count = (*connector_funcs->get_modes)(connector); if (!count) { - count = drm_add_modes_noedid(connector, 800, 600); + count = drm_add_modes_noedid(connector, 1024, 768); if (!count) return 0; } diff --git a/drivers/gpu/drm/i915/intel_dp_i2c.c b/drivers/gpu/drm/drm_dp_i2c_helper.c index a63b6f57d2d..f1c7c856e9d 100644 --- a/drivers/gpu/drm/i915/intel_dp_i2c.c +++ b/drivers/gpu/drm/drm_dp_i2c_helper.c @@ -28,84 +28,20 @@ #include <linux/errno.h> #include <linux/sched.h> #include <linux/i2c.h> -#include "intel_dp.h" +#include "drm_dp_helper.h" #include "drmP.h" /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ - -#define MODE_I2C_START 1 -#define MODE_I2C_WRITE 2 -#define MODE_I2C_READ 4 -#define MODE_I2C_STOP 8 - static int i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, uint8_t write_byte, uint8_t *read_byte) { struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - uint16_t address = algo_data->address; - uint8_t msg[5]; - uint8_t reply[2]; - int msg_bytes; - int reply_bytes; int ret; - - /* Set up the command byte */ - if (mode & MODE_I2C_READ) - msg[0] = AUX_I2C_READ << 4; - else - msg[0] = AUX_I2C_WRITE << 4; - - if (!(mode & MODE_I2C_STOP)) - msg[0] |= AUX_I2C_MOT << 4; - - msg[1] = address >> 8; - msg[2] = address; - - switch (mode) { - case MODE_I2C_WRITE: - msg[3] = 0; - msg[4] = write_byte; - msg_bytes = 5; - reply_bytes = 1; - break; - case MODE_I2C_READ: - msg[3] = 0; - msg_bytes = 4; - reply_bytes = 2; - break; - default: - msg_bytes = 3; - reply_bytes = 1; - break; - } - - for (;;) { - ret = (*algo_data->aux_ch)(adapter, - msg, msg_bytes, - reply, reply_bytes); - if (ret < 0) { - DRM_DEBUG("aux_ch failed %d\n", ret); - return ret; - } - switch (reply[0] & AUX_I2C_REPLY_MASK) { - case AUX_I2C_REPLY_ACK: - if (mode == MODE_I2C_READ) { - *read_byte = reply[1]; - } - return reply_bytes - 1; - case AUX_I2C_REPLY_NACK: - DRM_DEBUG("aux_ch nack\n"); - return -EREMOTEIO; - case AUX_I2C_REPLY_DEFER: - DRM_DEBUG("aux_ch defer\n"); - udelay(100); - break; - default: - DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]); - return -EREMOTEIO; - } - } + + ret = (*algo_data->aux_ch)(adapter, mode, + write_byte, read_byte); + return ret; } /* diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index a75ca63deea..ff2f1042cb4 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -145,6 +145,8 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW) }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) @@ -366,6 +368,29 @@ module_init(drm_core_init); module_exit(drm_core_exit); /** + * Copy and IOCTL return string to user space + */ +static int drm_copy_field(char *buf, size_t *buf_len, const char *value) +{ + int len; + + /* don't overflow userbuf */ + len = strlen(value); + if (len > *buf_len) + len = *buf_len; + + /* let userspace know exact length of driver value (which could be + * larger than the userspace-supplied buffer) */ + *buf_len = strlen(value); + + /* finally, try filling in the userbuf */ + if (len && buf) + if (copy_to_user(buf, value, len)) + return -EFAULT; + return 0; +} + +/** * Get version information * * \param inode device inode. @@ -380,16 +405,21 @@ static int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_version *version = data; - int len; + int err; version->version_major = dev->driver->major; version->version_minor = dev->driver->minor; version->version_patchlevel = dev->driver->patchlevel; - DRM_COPY(version->name, dev->driver->name); - DRM_COPY(version->date, dev->driver->date); - DRM_COPY(version->desc, dev->driver->desc); - - return 0; + err = drm_copy_field(version->name, &version->name_len, + dev->driver->name); + if (!err) + err = drm_copy_field(version->date, &version->date_len, + dev->driver->date); + if (!err) + err = drm_copy_field(version->desc, &version->desc_len, + dev->driver->desc); + + return err; } /** diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b54ba63d506..c39b26f1abe 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -123,18 +123,20 @@ static const u8 edid_header[] = { */ static bool edid_is_valid(struct edid *edid) { - int i; + int i, score = 0; u8 csum = 0; u8 *raw_edid = (u8 *)edid; - if (memcmp(edid->header, edid_header, sizeof(edid_header))) - goto bad; - if (edid->version != 1) { - DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); + for (i = 0; i < sizeof(edid_header); i++) + if (raw_edid[i] == edid_header[i]) + score++; + + if (score == 8) ; + else if (score >= 6) { + DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); + memcpy(raw_edid, edid_header, sizeof(edid_header)); + } else goto bad; - } - if (edid->revision > 4) - DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); for (i = 0; i < EDID_LENGTH; i++) csum += raw_edid[i]; @@ -143,6 +145,14 @@ static bool edid_is_valid(struct edid *edid) goto bad; } + if (edid->version != 1) { + DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); + goto bad; + } + + if (edid->revision > 4) + DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); + return 1; bad: @@ -481,16 +491,17 @@ static struct drm_display_mode drm_dmt_modes[] = { 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; +static const int drm_num_dmt_modes = + sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); static struct drm_display_mode *drm_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh) { - int i, count; + int i; struct drm_display_mode *ptr, *mode; - count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); mode = NULL; - for (i = 0; i < count; i++) { + for (i = 0; i < drm_num_dmt_modes; i++) { ptr = &drm_dmt_modes[i]; if (hsize == ptr->hdisplay && vsize == ptr->vdisplay && @@ -834,8 +845,165 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid return modes; } +/* + * XXX fix this for: + * - GTF secondary curve formula + * - EDID 1.4 range offsets + * - CVT extended bits + */ +static bool +mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing) +{ + struct detailed_data_monitor_range *range; + int hsync, vrefresh; + + range = &timing->data.other_data.data.range; + + hsync = drm_mode_hsync(mode); + vrefresh = drm_mode_vrefresh(mode); + + if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz) + return false; + + if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq) + return false; + + if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) { + /* be forgiving since it's in units of 10MHz */ + int max_clock = range->pixel_clock_mhz * 10 + 9; + max_clock *= 1000; + if (mode->clock > max_clock) + return false; + } + + return true; +} + +/* + * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will + * need to account for them. + */ +static int drm_gtf_modes_for_range(struct drm_connector *connector, + struct detailed_timing *timing) +{ + int i, modes = 0; + struct drm_display_mode *newmode; + struct drm_device *dev = connector->dev; + + for (i = 0; i < drm_num_dmt_modes; i++) { + if (mode_in_range(drm_dmt_modes + i, timing)) { + newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + } + + return modes; +} + +static int drm_cvt_modes(struct drm_connector *connector, + struct detailed_timing *timing) +{ + int i, j, modes = 0; + struct drm_display_mode *newmode; + struct drm_device *dev = connector->dev; + struct cvt_timing *cvt; + const int rates[] = { 60, 85, 75, 60, 50 }; + + for (i = 0; i < 4; i++) { + int width, height; + cvt = &(timing->data.other_data.data.cvt[i]); + + height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 8) + 1) * 2; + switch (cvt->code[1] & 0xc0) { + case 0x00: + width = height * 4 / 3; + break; + case 0x40: + width = height * 16 / 9; + break; + case 0x80: + width = height * 16 / 10; + break; + case 0xc0: + width = height * 15 / 9; + break; + } + + for (j = 1; j < 5; j++) { + if (cvt->code[2] & (1 << j)) { + newmode = drm_cvt_mode(dev, width, height, + rates[j], j == 0, + false, false); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + } + } + + return modes; +} + +static int add_detailed_modes(struct drm_connector *connector, + struct detailed_timing *timing, + struct edid *edid, u32 quirks, int preferred) +{ + int i, modes = 0; + struct detailed_non_pixel *data = &timing->data.other_data; + int timing_level = standard_timing_level(edid); + int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF); + struct drm_display_mode *newmode; + struct drm_device *dev = connector->dev; + + if (timing->pixel_clock) { + newmode = drm_mode_detailed(dev, edid, timing, quirks); + if (!newmode) + return 0; + + if (preferred) + newmode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, newmode); + return 1; + } + + /* other timing types */ + switch (data->type) { + case EDID_DETAIL_MONITOR_RANGE: + if (gtf) + modes += drm_gtf_modes_for_range(connector, timing); + break; + case EDID_DETAIL_STD_MODES: + /* Six modes per detailed section */ + for (i = 0; i < 6; i++) { + struct std_timing *std; + struct drm_display_mode *newmode; + + std = &data->data.timings[i]; + newmode = drm_mode_std(dev, std, edid->revision, + timing_level); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + break; + case EDID_DETAIL_CVT_3BYTE: + modes += drm_cvt_modes(connector, timing); + break; + default: + break; + } + + return modes; +} + /** - * add_detailed_modes - get detailed mode info from EDID data + * add_detailed_info - get detailed mode info from EDID data * @connector: attached connector * @edid: EDID block to scan * @quirks: quirks to apply @@ -846,67 +1014,24 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid static int add_detailed_info(struct drm_connector *connector, struct edid *edid, u32 quirks) { - struct drm_device *dev = connector->dev; - int i, j, modes = 0; - int timing_level; - - timing_level = standard_timing_level(edid); + int i, modes = 0; for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { struct detailed_timing *timing = &edid->detailed_timings[i]; - struct detailed_non_pixel *data = &timing->data.other_data; - struct drm_display_mode *newmode; - - /* X server check is version 1.1 or higher */ - if (edid->version == 1 && edid->revision >= 1 && - !timing->pixel_clock) { - /* Other timing or info */ - switch (data->type) { - case EDID_DETAIL_MONITOR_SERIAL: - break; - case EDID_DETAIL_MONITOR_STRING: - break; - case EDID_DETAIL_MONITOR_RANGE: - /* Get monitor range data */ - break; - case EDID_DETAIL_MONITOR_NAME: - break; - case EDID_DETAIL_MONITOR_CPDATA: - break; - case EDID_DETAIL_STD_MODES: - for (j = 0; j < 6; i++) { - struct std_timing *std; - struct drm_display_mode *newmode; - - std = &data->data.timings[j]; - newmode = drm_mode_std(dev, std, - edid->revision, - timing_level); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - break; - default: - break; - } - } else { - newmode = drm_mode_detailed(dev, edid, timing, quirks); - if (!newmode) - continue; + int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); - /* First detailed mode is preferred */ - if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING)) - newmode->type |= DRM_MODE_TYPE_PREFERRED; - drm_mode_probed_add(connector, newmode); + /* In 1.0, only timings are allowed */ + if (!timing->pixel_clock && edid->version == 1 && + edid->revision == 0) + continue; - modes++; - } + modes += add_detailed_modes(connector, timing, edid, quirks, + preferred); } return modes; } + /** * add_detailed_mode_eedid - get detailed mode info from addtional timing * EDID block @@ -920,12 +1045,9 @@ static int add_detailed_info(struct drm_connector *connector, static int add_detailed_info_eedid(struct drm_connector *connector, struct edid *edid, u32 quirks) { - struct drm_device *dev = connector->dev; - int i, j, modes = 0; + int i, modes = 0; char *edid_ext = NULL; struct detailed_timing *timing; - struct detailed_non_pixel *data; - struct drm_display_mode *newmode; int edid_ext_num; int start_offset, end_offset; int timing_level; @@ -976,51 +1098,7 @@ static int add_detailed_info_eedid(struct drm_connector *connector, for (i = start_offset; i < end_offset; i += sizeof(struct detailed_timing)) { timing = (struct detailed_timing *)(edid_ext + i); - data = &timing->data.other_data; - /* Detailed mode timing */ - if (timing->pixel_clock) { - newmode = drm_mode_detailed(dev, edid, timing, quirks); - if (!newmode) - continue; - - drm_mode_probed_add(connector, newmode); - - modes++; - continue; - } - - /* Other timing or info */ - switch (data->type) { - case EDID_DETAIL_MONITOR_SERIAL: - break; - case EDID_DETAIL_MONITOR_STRING: - break; - case EDID_DETAIL_MONITOR_RANGE: - /* Get monitor range data */ - break; - case EDID_DETAIL_MONITOR_NAME: - break; - case EDID_DETAIL_MONITOR_CPDATA: - break; - case EDID_DETAIL_STD_MODES: - /* Five modes per detailed section */ - for (j = 0; j < 5; i++) { - struct std_timing *std; - struct drm_display_mode *newmode; - - std = &data->data.timings[j]; - newmode = drm_mode_std(dev, std, - edid->revision, - timing_level); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - break; - default: - break; - } + modes += add_detailed_modes(connector, timing, edid, quirks, 0); } return modes; @@ -1066,19 +1144,19 @@ static int drm_ddc_read_edid(struct drm_connector *connector, struct i2c_adapter *adapter, char *buf, int len) { - int ret; + int i; - ret = drm_do_probe_ddc_edid(adapter, buf, len); - if (ret != 0) { - goto end; - } - if (!edid_is_valid((struct edid *)buf)) { - dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", - drm_get_connector_name(connector)); - ret = -1; + for (i = 0; i < 4; i++) { + if (drm_do_probe_ddc_edid(adapter, buf, len)) + return -1; + if (edid_is_valid((struct edid *)buf)) + return 0; } -end: - return ret; + + /* repeated checksum failures; warn, but carry on */ + dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", + drm_get_connector_name(connector)); + return -1; } /** @@ -1296,6 +1374,8 @@ int drm_add_modes_noedid(struct drm_connector *connector, ptr->vdisplay > vdisplay) continue; } + if (drm_mode_vrefresh(ptr) > 61) + continue; mode = drm_mode_duplicate(dev, ptr); if (mode) { drm_mode_probed_add(connector, mode); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 65ef011fa8b..34afe15ebc0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -373,11 +373,9 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) mutex_unlock(&dev->mode_config.mutex); } } - if (dpms_mode == DRM_MODE_DPMS_OFF) { - mutex_lock(&dev->mode_config.mutex); - crtc_funcs->dpms(crtc, dpms_mode); - mutex_unlock(&dev->mode_config.mutex); - } + mutex_lock(&dev->mode_config.mutex); + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + mutex_unlock(&dev->mode_config.mutex); } } } @@ -385,18 +383,23 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) int drm_fb_helper_blank(int blank, struct fb_info *info) { switch (blank) { + /* Display: On; HSync: On, VSync: On */ case FB_BLANK_UNBLANK: drm_fb_helper_on(info); break; + /* Display: Off; HSync: On, VSync: On */ case FB_BLANK_NORMAL: - drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); + drm_fb_helper_off(info, DRM_MODE_DPMS_ON); break; + /* Display: Off; HSync: Off, VSync: On */ case FB_BLANK_HSYNC_SUSPEND: drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); break; + /* Display: Off; HSync: On, VSync: Off */ case FB_BLANK_VSYNC_SUSPEND: drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); break; + /* Display: Off; HSync: Off, VSync: Off */ case FB_BLANK_POWERDOWN: drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); break; @@ -905,8 +908,13 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, if (new_fb) { info->var.pixclock = 0; - if (register_framebuffer(info) < 0) + ret = fb_alloc_cmap(&info->cmap, crtc->gamma_size, 0); + if (ret) + return ret; + if (register_framebuffer(info) < 0) { + fb_dealloc_cmap(&info->cmap); return -EINVAL; + } } else { drm_fb_helper_set_par(info); } @@ -936,6 +944,7 @@ void drm_fb_helper_free(struct drm_fb_helper *helper) unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); } drm_fb_helper_crtc_free(helper); + fb_dealloc_cmap(&helper->fb->fbdev->cmap); } EXPORT_SYMBOL(drm_fb_helper_free); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 251bc0e3b5e..08d14df3bb4 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp, INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); + INIT_LIST_HEAD(&priv->event_list); + init_waitqueue_head(&priv->event_wait); + priv->event_space = 4096; /* set aside 4k for event buffer */ if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); @@ -297,6 +300,18 @@ static int drm_open_helper(struct inode *inode, struct file *filp, goto out_free; } } + mutex_lock(&dev->struct_mutex); + if (dev->driver->master_set) { + ret = dev->driver->master_set(dev, priv, true); + if (ret) { + /* drop both references if this fails */ + drm_master_put(&priv->minor->master); + drm_master_put(&priv->master); + mutex_unlock(&dev->struct_mutex); + goto out_free; + } + } + mutex_unlock(&dev->struct_mutex); } else { /* get a reference to the master */ priv->master = drm_master_get(priv->minor->master); @@ -413,6 +428,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp) } } +static void drm_events_release(struct drm_file *file_priv) +{ + struct drm_device *dev = file_priv->minor->dev; + struct drm_pending_event *e, *et; + struct drm_pending_vblank_event *v, *vt; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); |