You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'drm-core-next' into drm-linus
Bring all core drm changes into 2.6.32 tree and resolve the conflict that occurs. Conflicts: drivers/gpu/drm/drm_fb_helper.c
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -801,6 +810,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -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 )
|
||||
@@ -365,6 +367,29 @@ static void __exit drm_core_exit(void)
|
||||
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
|
||||
*
|
||||
@@ -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);
|
||||
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 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+204
-124
@@ -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)))
|
||||
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->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");
|
||||
|
||||
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;
|
||||
int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
|
||||
|
||||
/* 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;
|
||||
/* In 1.0, only timings are allowed */
|
||||
if (!timing->pixel_clock && edid->version == 1 &&
|
||||
edid->revision == 0)
|
||||
continue;
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
if (!edid_is_valid((struct edid *)buf)) {
|
||||
dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
|
||||
drm_get_connector_name(connector));
|
||||
ret = -1;
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -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, modeset->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);
|
||||
|
||||
|
||||
+110
-2
@@ -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);
|
||||
|
||||
/* Remove pending flips */
|
||||
list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
|
||||
if (v->base.file_priv == file_priv) {
|
||||
list_del(&v->base.link);
|
||||
drm_vblank_put(dev, v->pipe);
|
||||
v->base.destroy(&v->base);
|
||||
}
|
||||
|
||||
/* Remove unconsumed events */
|
||||
list_for_each_entry_safe(e, et, &file_priv->event_list, link)
|
||||
e->destroy(e);
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release file.
|
||||
*
|
||||
@@ -451,6 +490,8 @@ int drm_release(struct inode *inode, struct file *filp)
|
||||
if (file_priv->minor->master)
|
||||
drm_master_release(dev, filp);
|
||||
|
||||
drm_events_release(file_priv);
|
||||
|
||||
if (dev->driver->driver_features & DRIVER_GEM)
|
||||
drm_gem_release(dev, file_priv);
|
||||
|
||||
@@ -504,6 +545,8 @@ int drm_release(struct inode *inode, struct file *filp)
|
||||
|
||||
if (file_priv->minor->master == file_priv->master) {
|
||||
/* drop the reference held my the minor */
|
||||
if (dev->driver->master_drop)
|
||||
dev->driver->master_drop(dev, file_priv, true);
|
||||
drm_master_put(&file_priv->minor->master);
|
||||
}
|
||||
}
|
||||
@@ -544,9 +587,74 @@ int drm_release(struct inode *inode, struct file *filp)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_release);
|
||||
|
||||
/** No-op. */
|
||||
static bool
|
||||
drm_dequeue_event(struct drm_file *file_priv,
|
||||
size_t total, size_t max, struct drm_pending_event **out)
|
||||
{
|
||||
struct drm_device *dev = file_priv->minor->dev;
|
||||
struct drm_pending_event *e;
|
||||
unsigned long flags;
|
||||
bool ret = false;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
*out = NULL;
|
||||
if (list_empty(&file_priv->event_list))
|
||||
goto out;
|
||||
e = list_first_entry(&file_priv->event_list,
|
||||
struct drm_pending_event, link);
|
||||
if (e->event->length + total > max)
|
||||
goto out;
|
||||
|
||||
file_priv->event_space += e->event->length;
|
||||
list_del(&e->link);
|
||||
*out = e;
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t drm_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct drm_file *file_priv = filp->private_data;
|
||||
struct drm_pending_event *e;
|
||||
size_t total;
|
||||
ssize_t ret;
|
||||
|
||||
ret = wait_event_interruptible(file_priv->event_wait,
|
||||
!list_empty(&file_priv->event_list));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
total = 0;
|
||||
while (drm_dequeue_event(file_priv, total, count, &e)) {
|
||||
if (copy_to_user(buffer + total,
|
||||
e->event, e->event->length)) {
|
||||
total = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
total += e->event->length;
|
||||
e->destroy(e);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_read);
|
||||
|
||||
unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
|
||||
{
|
||||
return 0;
|
||||
struct drm_file *file_priv = filp->private_data;
|
||||
unsigned int mask = 0;
|
||||
|
||||
poll_wait(filp, &file_priv->event_wait, wait);
|
||||
|
||||
if (!list_empty(&file_priv->event_list))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
return mask;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_poll);
|
||||
|
||||
@@ -550,6 +550,63 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
|
||||
union drm_wait_vblank *vblwait,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_pending_vblank_event *e;
|
||||
struct timeval now;
|
||||
unsigned long flags;
|
||||
unsigned int seq;
|
||||
|
||||
e = kzalloc(sizeof *e, GFP_KERNEL);
|
||||
if (e == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
e->pipe = pipe;
|
||||
e->event.base.type = DRM_EVENT_VBLANK;
|
||||
e->event.base.length = sizeof e->event;
|
||||
e->event.user_data = vblwait->request.signal;
|
||||
e->base.event = &e->event.base;
|
||||
e->base.file_priv = file_priv;
|
||||
e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
|
||||
|
||||
do_gettimeofday(&now);
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (file_priv->event_space < sizeof e->event) {
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
kfree(e);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
file_priv->event_space -= sizeof e->event;
|
||||
seq = drm_vblank_count(dev, pipe);
|
||||
if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
|
||||
(seq - vblwait->request.sequence) <= (1 << 23)) {
|
||||
vblwait->request.sequence = seq + 1;
|
||||
vblwait->reply.sequence = vblwait->request.sequence;
|
||||
}
|
||||
|
||||
DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
|
||||
vblwait->request.sequence, seq, pipe);
|
||||
|
||||
e->event.sequence = vblwait->request.sequence;
|
||||
if ((seq - vblwait->request.sequence) <= (1 << 23)) {
|
||||
e->event.tv_sec = now.tv_sec;
|
||||
e->event.tv_usec = now.tv_usec;
|
||||
drm_vblank_put(dev, e->pipe);
|
||||
list_add_tail(&e->base.link, &e->base.file_priv->event_list);
|
||||
wake_up_interruptible(&e->base.file_priv->event_wait);
|
||||
} else {
|
||||
list_add_tail(&e->base.link, &dev->vblank_event_list);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for VBLANK.
|
||||
*
|
||||
@@ -609,6 +666,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (flags & _DRM_VBLANK_EVENT)
|
||||
return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
|
||||
|
||||
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
|
||||
(seq - vblwait->request.sequence) <= (1<<23)) {
|
||||
vblwait->request.sequence = seq + 1;
|
||||
@@ -641,6 +701,38 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void drm_handle_vblank_events(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct drm_pending_vblank_event *e, *t;
|
||||
struct timeval now;
|
||||
unsigned long flags;
|
||||
unsigned int seq;
|
||||
|
||||
do_gettimeofday(&now);
|
||||
seq = drm_vblank_count(dev, crtc);
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
|
||||
if (e->pipe != crtc)
|
||||
continue;
|
||||
if ((seq - e->event.sequence) > (1<<23))
|
||||
continue;
|
||||
|
||||
DRM_DEBUG("vblank event on %d, current %d\n",
|
||||
e->event.sequence, seq);
|
||||
|
||||
e->event.sequence = seq;
|
||||
e->event.tv_sec = now.tv_sec;
|
||||
e->event.tv_usec = now.tv_usec;
|
||||
drm_vblank_put(dev, e->pipe);
|
||||
list_move_tail(&e->base.link, &e->base.file_priv->event_list);
|
||||
wake_up_interruptible(&e->base.file_priv->event_wait);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_handle_vblank - handle a vblank event
|
||||
* @dev: DRM device
|
||||
@@ -651,7 +743,11 @@ done:
|
||||
*/
|
||||
void drm_handle_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
if (!dev->num_crtcs)
|
||||
return;
|
||||
|
||||
atomic_inc(&dev->_vblank_count[crtc]);
|
||||
DRM_WAKEUP(&dev->vbl_queue[crtc]);
|
||||
drm_handle_vblank_events(dev, crtc);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_handle_vblank);
|
||||
|
||||
@@ -395,7 +395,7 @@ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
|
||||
else
|
||||
total_used += entry->size;
|
||||
}
|
||||
seq_printf(m, "total: %d, used %d free %d\n", total, total_free, total_used);
|
||||
seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mm_dump_table);
|
||||
|
||||
@@ -553,6 +553,32 @@ int drm_mode_height(struct drm_display_mode *mode)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_height);
|
||||
|
||||
/** drm_mode_hsync - get the hsync of a mode
|
||||
* @mode: mode
|
||||
*
|
||||
* LOCKING:
|
||||
* None.
|
||||
*
|
||||
* Return @modes's hsync rate in kHz, rounded to the nearest int.
|
||||
*/
|
||||
int drm_mode_hsync(struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int calc_val;
|
||||
|
||||
if (mode->hsync)
|
||||
return mode->hsync;
|
||||
|
||||
if (mode->htotal < 0)
|
||||
return 0;
|
||||
|
||||
calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */
|
||||
calc_val += 500; /* round to 1000Hz */
|
||||
calc_val /= 1000; /* truncate to kHz */
|
||||
|
||||
return calc_val;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_hsync);
|
||||
|
||||
/**
|
||||
* drm_mode_vrefresh - get the vrefresh of a mode
|
||||
* @mode: mode
|
||||
@@ -560,7 +586,7 @@ EXPORT_SYMBOL(drm_mode_height);
|
||||
* LOCKING:
|
||||
* None.
|
||||
*
|
||||
* Return @mode's vrefresh rate or calculate it if necessary.
|
||||
* Return @mode's vrefresh rate in Hz or calculate it if necessary.
|
||||
*
|
||||
* FIXME: why is this needed? shouldn't vrefresh be set already?
|
||||
*
|
||||
|
||||
@@ -128,6 +128,7 @@ struct drm_master *drm_master_get(struct drm_master *master)
|
||||
kref_get(&master->refcount);
|
||||
return master;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_master_get);
|
||||
|
||||
static void drm_master_destroy(struct kref *kref)
|
||||
{
|
||||
@@ -170,10 +171,13 @@ void drm_master_put(struct drm_master **master)
|
||||
kref_put(&(*master)->refcount, drm_master_destroy);
|
||||
*master = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_master_put);
|
||||
|
||||
int drm_setmaster_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (file_priv->is_master)
|
||||
return 0;
|
||||
|
||||
@@ -188,6 +192,13 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
file_priv->minor->master = drm_master_get(file_priv->master);
|
||||
file_priv->is_master = 1;
|
||||
if (dev->driver->master_set) {
|
||||
ret = dev->driver->master_set(dev, file_priv, false);
|
||||
if (unlikely(ret != 0)) {
|
||||
file_priv->is_master = 0;
|
||||
drm_master_put(&file_priv->minor->master);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
@@ -204,6 +215,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (dev->driver->master_drop)
|
||||
dev->driver->master_drop(dev, file_priv, false);
|
||||
drm_master_put(&file_priv->minor->master);
|
||||
file_priv->is_master = 0;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
@@ -220,9 +233,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
|
||||
INIT_LIST_HEAD(&dev->ctxlist);
|
||||
INIT_LIST_HEAD(&dev->vmalist);
|
||||
INIT_LIST_HEAD(&dev->maplist);
|
||||
INIT_LIST_HEAD(&dev->vblank_event_list);
|
||||
|
||||
spin_lock_init(&dev->count_lock);
|
||||
spin_lock_init(&dev->drw_lock);
|
||||
spin_lock_init(&dev->event_lock);
|
||||
init_timer(&dev->timer);
|
||||
mutex_init(&dev->struct_mutex);
|
||||
mutex_init(&dev->ctxlist_mutex);
|
||||
|
||||
@@ -15,7 +15,6 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
|
||||
intel_lvds.o \
|
||||
intel_bios.o \
|
||||
intel_dp.o \
|
||||
intel_dp_i2c.o \
|
||||
intel_hdmi.o \
|
||||
intel_sdvo.o \
|
||||
intel_modes.o \
|
||||
|
||||
@@ -333,6 +333,7 @@ static struct drm_driver driver = {
|
||||
.mmap = drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.read = drm_read,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = i915_compat_ioctl,
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "intel_drv.h"
|
||||
#include "i915_drm.h"
|
||||
#include "i915_drv.h"
|
||||
#include "intel_dp.h"
|
||||
#include "drm_dp_helper.h"
|
||||
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "intel_drv.h"
|
||||
#include "i915_drm.h"
|
||||
#include "i915_drv.h"
|
||||
#include "intel_dp.h"
|
||||
#include "drm_dp_helper.h"
|
||||
|
||||
#define DP_LINK_STATUS_SIZE 6
|
||||
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
|
||||
@@ -382,17 +382,77 @@ intel_dp_aux_native_read(struct intel_output *intel_output,
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_i2c_aux_ch(struct i2c_adapter *adapter,
|
||||
uint8_t *send, int send_bytes,
|
||||
uint8_t *recv, int recv_bytes)
|
||||
intel_dp_i2c_aux_ch(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;
|
||||
struct intel_dp_priv *dp_priv = container_of(adapter,
|
||||
struct intel_dp_priv,
|
||||
adapter);
|
||||
struct intel_output *intel_output = dp_priv->intel_output;
|
||||
uint16_t address = algo_data->address;
|
||||
uint8_t msg[5];
|
||||
uint8_t reply[2];
|
||||
int msg_bytes;
|
||||
int reply_bytes;
|
||||
int ret;
|
||||
|
||||
return intel_dp_aux_ch(intel_output,
|
||||
send, send_bytes, recv, recv_bytes);
|
||||
/* 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 = intel_dp_aux_ch(intel_output,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
ccflags-y := -Iinclude/drm
|
||||
ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
|
||||
ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o
|
||||
ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o \
|
||||
ttm_object.o ttm_lock.o ttm_execbuf_util.o
|
||||
|
||||
obj-$(CONFIG_DRM_TTM) += ttm.o
|
||||
|
||||
@@ -275,9 +275,10 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
|
||||
bo->ttm = ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT,
|
||||
page_flags | TTM_PAGE_FLAG_USER,
|
||||
glob->dummy_read_page);
|
||||
if (unlikely(bo->ttm == NULL))
|
||||
if (unlikely(bo->ttm == NULL)) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ttm_tt_set_user(bo->ttm, current,
|
||||
bo->buffer_start, bo->num_pages);
|
||||
|
||||
@@ -369,6 +369,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
|
||||
#endif
|
||||
return tmp;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_io_prot);
|
||||
|
||||
static int ttm_bo_ioremap(struct ttm_buffer_object *bo,
|
||||
unsigned long bus_base,
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#include "ttm/ttm_execbuf_util.h"
|
||||
#include "ttm/ttm_bo_driver.h"
|
||||
#include "ttm/ttm_placement.h"
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
void ttm_eu_backoff_reservation(struct list_head *list)
|
||||
{
|
||||
struct ttm_validate_buffer *entry;
|
||||
|
||||
list_for_each_entry(entry, list, head) {
|
||||
struct ttm_buffer_object *bo = entry->bo;
|
||||
if (!entry->reserved)
|
||||
continue;
|
||||
|
||||
entry->reserved = false;
|
||||
ttm_bo_unreserve(bo);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_eu_backoff_reservation);
|
||||
|
||||
/*
|
||||
* Reserve buffers for validation.
|
||||
*
|
||||
* If a buffer in the list is marked for CPU access, we back off and
|
||||
* wait for that buffer to become free for GPU access.
|
||||
*
|
||||
* If a buffer is reserved for another validation, the validator with
|
||||
* the highest validation sequence backs off and waits for that buffer
|
||||
* to become unreserved. This prevents deadlocks when validating multiple
|
||||
* buffers in different orders.
|
||||
*/
|
||||
|
||||
int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq)
|
||||
{
|
||||
struct ttm_validate_buffer *entry;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
list_for_each_entry(entry, list, head) {
|
||||
struct ttm_buffer_object *bo = entry->bo;
|
||||
|
||||
entry->reserved = false;
|
||||
ret = ttm_bo_reserve(bo, true, false, true, val_seq);
|
||||
if (ret != 0) {
|
||||
ttm_eu_backoff_reservation(list);
|
||||
if (ret == -EAGAIN) {
|
||||
ret = ttm_bo_wait_unreserved(bo, true);
|
||||
if (unlikely(ret != 0))
|
||||
return ret;
|
||||
goto retry;
|
||||
} else
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry->reserved = true;
|
||||
if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
|
||||
ttm_eu_backoff_reservation(list);
|
||||
ret = ttm_bo_wait_cpu(bo, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_eu_reserve_buffers);
|
||||
|
||||
void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj)
|
||||
{
|
||||
struct ttm_validate_buffer *entry;
|
||||
|
||||
list_for_each_entry(entry, list, head) {
|
||||
struct ttm_buffer_object *bo = entry->bo;
|
||||
struct ttm_bo_driver *driver = bo->bdev->driver;
|
||||
void *old_sync_obj;
|
||||
|
||||
spin_lock(&bo->lock);
|
||||
old_sync_obj = bo->sync_obj;
|
||||
bo->sync_obj = driver->sync_obj_ref(sync_obj);
|
||||
bo->sync_obj_arg = entry->new_sync_obj_arg;
|
||||
spin_unlock(&bo->lock);
|
||||
ttm_bo_unreserve(bo);
|
||||
entry->reserved = false;
|
||||
if (old_sync_obj)
|
||||
driver->sync_obj_unref(&old_sync_obj);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_eu_fence_buffer_objects);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user