31,16 → 31,12 |
*/ |
#include <linux/list.h> |
#include <linux/slab.h> |
#include "drm.h" |
#include "drmP.h" |
#include "drm_crtc.h" |
#include "drm_edid.h" |
#include <linux/export.h> |
#include <drm/drmP.h> |
#include <drm/drm_crtc.h> |
#include <drm/drm_edid.h> |
#include <drm/drm_fourcc.h> |
|
struct drm_prop_enum_list { |
int type; |
char *name; |
}; |
|
/* Avoid boilerplate. I'm tired of typing. */ |
#define DRM_ENUM_NAME_FN(fnname, list) \ |
char *fnname(int val) \ |
162,6 → 158,7 |
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 }, |
{ DRM_MODE_CONNECTOR_TV, "TV", 0 }, |
{ DRM_MODE_CONNECTOR_eDP, "eDP", 0 }, |
{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0}, |
}; |
|
static struct drm_prop_enum_list drm_encoder_enum_list[] = |
170,6 → 167,7 |
{ DRM_MODE_ENCODER_TMDS, "TMDS" }, |
{ DRM_MODE_ENCODER_LVDS, "LVDS" }, |
{ DRM_MODE_ENCODER_TVDAC, "TV" }, |
{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, |
}; |
|
char *drm_get_encoder_name(struct drm_encoder *encoder) |
228,7 → 226,7 |
again: |
if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { |
DRM_ERROR("Ran out memory getting a mode number\n"); |
return -EINVAL; |
return -ENOMEM; |
} |
|
mutex_lock(&dev->mode_config.idr_mutex); |
236,6 → 234,8 |
mutex_unlock(&dev->mode_config.idr_mutex); |
if (ret == -EAGAIN) |
goto again; |
else if (ret) |
return ret; |
|
obj->id = new_id; |
obj->type = obj_type; |
294,9 → 294,8 |
int ret; |
|
ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); |
if (ret) { |
if (ret) |
return ret; |
} |
|
fb->dev = dev; |
fb->funcs = funcs; |
320,23 → 319,13 |
void drm_framebuffer_cleanup(struct drm_framebuffer *fb) |
{ |
struct drm_device *dev = fb->dev; |
struct drm_crtc *crtc; |
struct drm_mode_set set; |
int ret; |
|
/* remove from any CRTC */ |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->fb == fb) { |
/* should turn off the crtc */ |
memset(&set, 0, sizeof(struct drm_mode_set)); |
set.crtc = crtc; |
set.fb = NULL; |
ret = crtc->funcs->set_config(&set); |
if (ret) |
DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); |
} |
} |
|
/* |
* This could be moved to drm_framebuffer_remove(), but for |
* debugging is nice to keep around the list of fb's that are |
* no longer associated w/ a drm_file but are not unreferenced |
* yet. (i915 and omapdrm have debugfs files which will show |
* this.) |
*/ |
drm_mode_object_put(dev, &fb->base); |
list_del(&fb->head); |
dev->mode_config.num_fb--; |
343,6 → 332,10 |
} |
EXPORT_SYMBOL(drm_framebuffer_cleanup); |
|
|
|
|
|
/** |
* drm_crtc_init - Initialise a new CRTC object |
* @dev: DRM device |
350,22 → 343,37 |
* @funcs: callbacks for the new CRTC |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* Takes mode_config lock. |
* |
* Inits a new object created as base part of an driver crtc object. |
* |
* RETURNS: |
* Zero on success, error code on failure. |
*/ |
void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
const struct drm_crtc_funcs *funcs) |
{ |
int ret; |
|
crtc->dev = dev; |
crtc->funcs = funcs; |
crtc->invert_dimensions = false; |
|
mutex_lock(&dev->mode_config.mutex); |
drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); |
|
ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); |
if (ret) |
goto out; |
|
crtc->base.properties = &crtc->properties; |
|
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); |
dev->mode_config.num_crtc++; |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_crtc_init); |
|
425,7 → 433,7 |
struct drm_display_mode *mode) |
{ |
list_del(&mode->head); |
kfree(mode); |
drm_mode_destroy(connector->dev, mode); |
} |
EXPORT_SYMBOL(drm_mode_remove); |
|
437,21 → 445,30 |
* @name: user visible name of the connector |
* |
* LOCKING: |
* Caller must hold @dev's mode_config lock. |
* Takes mode config lock. |
* |
* Initialises a preallocated connector. Connectors should be |
* subclassed as part of driver connector objects. |
* |
* RETURNS: |
* Zero on success, error code on failure. |
*/ |
void drm_connector_init(struct drm_device *dev, |
int drm_connector_init(struct drm_device *dev, |
struct drm_connector *connector, |
const struct drm_connector_funcs *funcs, |
int connector_type) |
{ |
int ret; |
|
mutex_lock(&dev->mode_config.mutex); |
|
ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); |
if (ret) |
goto out; |
|
connector->base.properties = &connector->properties; |
connector->dev = dev; |
connector->funcs = funcs; |
drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); |
connector->connector_type = connector_type; |
connector->connector_type_id = |
++drm_connector_enum_list[connector_type].count; /* TODO */ |
463,13 → 480,18 |
list_add_tail(&connector->head, &dev->mode_config.connector_list); |
dev->mode_config.num_connector++; |
|
if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) |
drm_connector_attach_property(connector, |
dev->mode_config.edid_property, 0); |
dev->mode_config.edid_property, |
0); |
|
drm_connector_attach_property(connector, |
dev->mode_config.dpms_property, 0); |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_connector_init); |
|
478,7 → 500,7 |
* @connector: connector to cleanup |
* |
* LOCKING: |
* Caller must hold @dev's mode_config lock. |
* Takes mode config lock. |
* |
* Cleans up the connector but doesn't free the object. |
*/ |
504,16 → 526,31 |
} |
EXPORT_SYMBOL(drm_connector_cleanup); |
|
void drm_encoder_init(struct drm_device *dev, |
void drm_connector_unplug_all(struct drm_device *dev) |
{ |
struct drm_connector *connector; |
|
/* taking the mode config mutex ends up in a clash with sysfs */ |
// list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
// drm_sysfs_connector_remove(connector); |
|
} |
EXPORT_SYMBOL(drm_connector_unplug_all); |
|
int drm_encoder_init(struct drm_device *dev, |
struct drm_encoder *encoder, |
const struct drm_encoder_funcs *funcs, |
int encoder_type) |
{ |
int ret; |
|
mutex_lock(&dev->mode_config.mutex); |
|
ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); |
if (ret) |
goto out; |
|
encoder->dev = dev; |
|
drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); |
encoder->encoder_type = encoder_type; |
encoder->funcs = funcs; |
|
520,7 → 557,10 |
list_add_tail(&encoder->head, &dev->mode_config.encoder_list); |
dev->mode_config.num_encoder++; |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_encoder_init); |
|
535,6 → 575,70 |
} |
EXPORT_SYMBOL(drm_encoder_cleanup); |
|
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, |
unsigned long possible_crtcs, |
const struct drm_plane_funcs *funcs, |
const uint32_t *formats, uint32_t format_count, |
bool priv) |
{ |
int ret; |
|
mutex_lock(&dev->mode_config.mutex); |
|
ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); |
if (ret) |
goto out; |
|
plane->base.properties = &plane->properties; |
plane->dev = dev; |
plane->funcs = funcs; |
plane->format_types = kmalloc(sizeof(uint32_t) * format_count, |
GFP_KERNEL); |
if (!plane->format_types) { |
DRM_DEBUG_KMS("out of memory when allocating plane\n"); |
drm_mode_object_put(dev, &plane->base); |
ret = -ENOMEM; |
goto out; |
} |
|
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); |
plane->format_count = format_count; |
plane->possible_crtcs = possible_crtcs; |
|
/* private planes are not exposed to userspace, but depending on |
* display hardware, might be convenient to allow sharing programming |
* for the scanout engine with the crtc implementation. |
*/ |
if (!priv) { |
list_add_tail(&plane->head, &dev->mode_config.plane_list); |
dev->mode_config.num_plane++; |
} else { |
INIT_LIST_HEAD(&plane->head); |
} |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_plane_init); |
|
void drm_plane_cleanup(struct drm_plane *plane) |
{ |
struct drm_device *dev = plane->dev; |
|
mutex_lock(&dev->mode_config.mutex); |
kfree(plane->format_types); |
drm_mode_object_put(dev, &plane->base); |
/* if not added to a list, it must be a private plane */ |
if (!list_empty(&plane->head)) { |
list_del(&plane->head); |
dev->mode_config.num_plane--; |
} |
mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_plane_cleanup); |
|
/** |
* drm_mode_create - create a new display mode |
* @dev: DRM device |
555,7 → 659,11 |
if (!nmode) |
return NULL; |
|
drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE); |
if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { |
kfree(nmode); |
return NULL; |
} |
|
return nmode; |
} |
EXPORT_SYMBOL(drm_mode_create); |
572,6 → 680,9 |
*/ |
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) |
{ |
if (!mode) |
return; |
|
drm_mode_object_put(dev, &mode->base); |
|
kfree(mode); |
582,7 → 693,6 |
{ |
struct drm_property *edid; |
struct drm_property *dpms; |
int i; |
|
/* |
* Standard properties (apply to all connectors) |
592,11 → 702,9 |
"EDID", 0); |
dev->mode_config.edid_property = edid; |
|
dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM, |
"DPMS", ARRAY_SIZE(drm_dpms_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) |
drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type, |
drm_dpms_enum_list[i].name); |
dpms = drm_property_create_enum(dev, 0, |
"DPMS", drm_dpms_enum_list, |
ARRAY_SIZE(drm_dpms_enum_list)); |
dev->mode_config.dpms_property = dpms; |
|
return 0; |
612,30 → 720,21 |
{ |
struct drm_property *dvi_i_selector; |
struct drm_property *dvi_i_subconnector; |
int i; |
|
if (dev->mode_config.dvi_i_select_subconnector_property) |
return 0; |
|
dvi_i_selector = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, |
drm_property_create_enum(dev, 0, |
"select subconnector", |
drm_dvi_i_select_enum_list, |
ARRAY_SIZE(drm_dvi_i_select_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++) |
drm_property_add_enum(dvi_i_selector, i, |
drm_dvi_i_select_enum_list[i].type, |
drm_dvi_i_select_enum_list[i].name); |
dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; |
|
dvi_i_subconnector = |
drm_property_create(dev, DRM_MODE_PROP_ENUM | |
DRM_MODE_PROP_IMMUTABLE, |
dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, |
"subconnector", |
drm_dvi_i_subconnector_enum_list, |
ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++) |
drm_property_add_enum(dvi_i_subconnector, i, |
drm_dvi_i_subconnector_enum_list[i].type, |
drm_dvi_i_subconnector_enum_list[i].name); |
dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; |
|
return 0; |
666,23 → 765,17 |
/* |
* Basic connector properties |
*/ |
tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM, |
tv_selector = drm_property_create_enum(dev, 0, |
"select subconnector", |
drm_tv_select_enum_list, |
ARRAY_SIZE(drm_tv_select_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++) |
drm_property_add_enum(tv_selector, i, |
drm_tv_select_enum_list[i].type, |
drm_tv_select_enum_list[i].name); |
dev->mode_config.tv_select_subconnector_property = tv_selector; |
|
tv_subconnector = |
drm_property_create(dev, DRM_MODE_PROP_ENUM | |
DRM_MODE_PROP_IMMUTABLE, "subconnector", |
drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, |
"subconnector", |
drm_tv_subconnector_enum_list, |
ARRAY_SIZE(drm_tv_subconnector_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++) |
drm_property_add_enum(tv_subconnector, i, |
drm_tv_subconnector_enum_list[i].type, |
drm_tv_subconnector_enum_list[i].name); |
dev->mode_config.tv_subconnector_property = tv_subconnector; |
|
/* |
689,28 → 782,16 |
* Other, TV specific properties: margins & TV modes. |
*/ |
dev->mode_config.tv_left_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"left margin", 2); |
dev->mode_config.tv_left_margin_property->values[0] = 0; |
dev->mode_config.tv_left_margin_property->values[1] = 100; |
drm_property_create_range(dev, 0, "left margin", 0, 100); |
|
dev->mode_config.tv_right_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"right margin", 2); |
dev->mode_config.tv_right_margin_property->values[0] = 0; |
dev->mode_config.tv_right_margin_property->values[1] = 100; |
drm_property_create_range(dev, 0, "right margin", 0, 100); |
|
dev->mode_config.tv_top_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"top margin", 2); |
dev->mode_config.tv_top_margin_property->values[0] = 0; |
dev->mode_config.tv_top_margin_property->values[1] = 100; |
drm_property_create_range(dev, 0, "top margin", 0, 100); |
|
dev->mode_config.tv_bottom_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"bottom margin", 2); |
dev->mode_config.tv_bottom_margin_property->values[0] = 0; |
dev->mode_config.tv_bottom_margin_property->values[1] = 100; |
drm_property_create_range(dev, 0, "bottom margin", 0, 100); |
|
dev->mode_config.tv_mode_property = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, |
720,40 → 801,22 |
i, modes[i]); |
|
dev->mode_config.tv_brightness_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"brightness", 2); |
dev->mode_config.tv_brightness_property->values[0] = 0; |
dev->mode_config.tv_brightness_property->values[1] = 100; |
drm_property_create_range(dev, 0, "brightness", 0, 100); |
|
dev->mode_config.tv_contrast_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"contrast", 2); |
dev->mode_config.tv_contrast_property->values[0] = 0; |
dev->mode_config.tv_contrast_property->values[1] = 100; |
drm_property_create_range(dev, 0, "contrast", 0, 100); |
|
dev->mode_config.tv_flicker_reduction_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"flicker reduction", 2); |
dev->mode_config.tv_flicker_reduction_property->values[0] = 0; |
dev->mode_config.tv_flicker_reduction_property->values[1] = 100; |
drm_property_create_range(dev, 0, "flicker reduction", 0, 100); |
|
dev->mode_config.tv_overscan_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"overscan", 2); |
dev->mode_config.tv_overscan_property->values[0] = 0; |
dev->mode_config.tv_overscan_property->values[1] = 100; |
drm_property_create_range(dev, 0, "overscan", 0, 100); |
|
dev->mode_config.tv_saturation_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"saturation", 2); |
dev->mode_config.tv_saturation_property->values[0] = 0; |
dev->mode_config.tv_saturation_property->values[1] = 100; |
drm_property_create_range(dev, 0, "saturation", 0, 100); |
|
dev->mode_config.tv_hue_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"hue", 2); |
dev->mode_config.tv_hue_property->values[0] = 0; |
dev->mode_config.tv_hue_property->values[1] = 100; |
drm_property_create_range(dev, 0, "hue", 0, 100); |
|
return 0; |
} |
769,18 → 832,14 |
int drm_mode_create_scaling_mode_property(struct drm_device *dev) |
{ |
struct drm_property *scaling_mode; |
int i; |
|
if (dev->mode_config.scaling_mode_property) |
return 0; |
|
scaling_mode = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode", |
drm_property_create_enum(dev, 0, "scaling mode", |
drm_scaling_mode_enum_list, |
ARRAY_SIZE(drm_scaling_mode_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) |
drm_property_add_enum(scaling_mode, i, |
drm_scaling_mode_enum_list[i].type, |
drm_scaling_mode_enum_list[i].name); |
|
dev->mode_config.scaling_mode_property = scaling_mode; |
|
798,18 → 857,14 |
int drm_mode_create_dithering_property(struct drm_device *dev) |
{ |
struct drm_property *dithering_mode; |
int i; |
|
if (dev->mode_config.dithering_mode_property) |
return 0; |
|
dithering_mode = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering", |
drm_property_create_enum(dev, 0, "dithering", |
drm_dithering_mode_enum_list, |
ARRAY_SIZE(drm_dithering_mode_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++) |
drm_property_add_enum(dithering_mode, i, |
drm_dithering_mode_enum_list[i].type, |
drm_dithering_mode_enum_list[i].name); |
dev->mode_config.dithering_mode_property = dithering_mode; |
|
return 0; |
826,20 → 881,15 |
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, |
drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, |
"dirty", |
drm_dirty_info_enum_list, |
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; |
866,6 → 916,7 |
INIT_LIST_HEAD(&dev->mode_config.encoder_list); |
INIT_LIST_HEAD(&dev->mode_config.property_list); |
INIT_LIST_HEAD(&dev->mode_config.property_blob_list); |
INIT_LIST_HEAD(&dev->mode_config.plane_list); |
idr_init(&dev->mode_config.crtc_idr); |
|
mutex_lock(&dev->mode_config.mutex); |
922,6 → 973,7 |
|
return 0; |
} |
EXPORT_SYMBOL(drm_mode_group_init_legacy_group); |
|
/** |
* drm_mode_config_cleanup - free up DRM mode_config info |
942,6 → 994,7 |
struct drm_encoder *encoder, *enct; |
struct drm_framebuffer *fb, *fbt; |
struct drm_property *property, *pt; |
struct drm_plane *plane, *plt; |
|
list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, |
head) { |
958,8 → 1011,9 |
drm_property_destroy(dev, property); |
} |
|
list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
fb->funcs->destroy(fb); |
list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, |
head) { |
plane->funcs->destroy(plane); |
} |
|
list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { |
966,6 → 1020,8 |
crtc->funcs->destroy(crtc); |
} |
|
idr_remove_all(&dev->mode_config.crtc_idr); |
idr_destroy(&dev->mode_config.crtc_idr); |
} |
EXPORT_SYMBOL(drm_mode_config_cleanup); |
|
980,9 → 1036,16 |
* Convert a drm_display_mode into a drm_mode_modeinfo structure to return to |
* the user. |
*/ |
void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, |
struct drm_display_mode *in) |
static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, |
const struct drm_display_mode *in) |
{ |
WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || |
in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || |
in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || |
in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || |
in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX, |
"timing values too large for mode info\n"); |
|
out->clock = in->clock; |
out->hdisplay = in->hdisplay; |
out->hsync_start = in->hsync_start; |
1011,10 → 1074,16 |
* |
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to |
* the caller. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
void drm_crtc_convert_umode(struct drm_display_mode *out, |
struct drm_mode_modeinfo *in) |
static int drm_crtc_convert_umode(struct drm_display_mode *out, |
const struct drm_mode_modeinfo *in) |
{ |
if (in->clock > INT_MAX || in->vrefresh > INT_MAX) |
return -ERANGE; |
|
out->clock = in->clock; |
out->hdisplay = in->hdisplay; |
out->hsync_start = in->hsync_start; |
1031,6 → 1100,8 |
out->type = in->type; |
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); |
out->name[DRM_DISPLAY_MODE_LEN-1] = 0; |
|
return 0; |
} |
|
|
1231,7 → 1302,7 |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* Takes mode config lock. |
* |
* Construct a CRTC configuration structure to return to the user. |
* |
1291,7 → 1362,7 |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* Takes mode config lock. |
* |
* Construct a connector configuration structure to return to the user. |
* |
1336,11 → 1407,7 |
} |
connector = obj_to_connector(obj); |
|
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] != 0) { |
props_count++; |
} |
} |
props_count = connector->properties.count; |
|
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] != 0) { |
1376,7 → 1443,7 |
*/ |
if ((out_resp->count_modes >= mode_count) && mode_count) { |
copied = 0; |
mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr; |
mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; |
list_for_each_entry(mode, &connector->modes, head) { |
drm_crtc_convert_to_umode(&u_mode, mode); |
if (copy_to_user(mode_ptr + copied, |
1391,17 → 1458,16 |
|
if ((out_resp->count_props >= props_count) && props_count) { |
copied = 0; |
prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr); |
prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr); |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] != 0) { |
if (put_user(connector->property_ids[i], |
prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); |
prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); |
for (i = 0; i < connector->properties.count; i++) { |
if (put_user(connector->properties.ids[i], |
prop_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
|
if (put_user(connector->property_values[i], |
if (put_user(connector->properties.values[i], |
prop_values + copied)) { |
ret = -EFAULT; |
goto out; |
1409,12 → 1475,11 |
copied++; |
} |
} |
} |
out_resp->count_props = props_count; |
|
if ((out_resp->count_encoders >= encoders_count) && encoders_count) { |
copied = 0; |
encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr); |
encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] != 0) { |
if (put_user(connector->encoder_ids[i], |
1468,6 → 1533,254 |
} |
|
/** |
* drm_mode_getplane_res - get plane info |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Return an plane count and set of IDs. |
*/ |
int drm_mode_getplane_res(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_plane_res *plane_resp = data; |
struct drm_mode_config *config; |
struct drm_plane *plane; |
uint32_t __user *plane_ptr; |
int copied = 0, ret = 0; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
mutex_lock(&dev->mode_config.mutex); |
config = &dev->mode_config; |
|
/* |
* This ioctl is called twice, once to determine how much space is |
* needed, and the 2nd time to fill it. |
*/ |
if (config->num_plane && |
(plane_resp->count_planes >= config->num_plane)) { |
plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; |
|
list_for_each_entry(plane, &config->plane_list, head) { |
if (put_user(plane->base.id, plane_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
plane_resp->count_planes = config->num_plane; |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
|
/** |
* drm_mode_getplane - get plane info |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Return plane info, including formats supported, gamma size, any |
* current fb, etc. |
*/ |
int drm_mode_getplane(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_plane *plane_resp = data; |
struct drm_mode_object *obj; |
struct drm_plane *plane; |
uint32_t __user *format_ptr; |
int ret = 0; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, plane_resp->plane_id, |
DRM_MODE_OBJECT_PLANE); |
if (!obj) { |
ret = -ENOENT; |
goto out; |
} |
plane = obj_to_plane(obj); |
|
if (plane->crtc) |
plane_resp->crtc_id = plane->crtc->base.id; |
else |
plane_resp->crtc_id = 0; |
|
if (plane->fb) |
plane_resp->fb_id = plane->fb->base.id; |
else |
plane_resp->fb_id = 0; |
|
plane_resp->plane_id = plane->base.id; |
plane_resp->possible_crtcs = plane->possible_crtcs; |
plane_resp->gamma_size = plane->gamma_size; |
|
/* |
* This ioctl is called twice, once to determine how much space is |
* needed, and the 2nd time to fill it. |
*/ |
if (plane->format_count && |
(plane_resp->count_format_types >= plane->format_count)) { |
format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; |
if (copy_to_user(format_ptr, |
plane->format_types, |
sizeof(uint32_t) * plane->format_count)) { |
ret = -EFAULT; |
goto out; |
} |
} |
plane_resp->count_format_types = plane->format_count; |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
|
/** |
* drm_mode_setplane - set up or tear down an plane |
* @dev: DRM device |
* @data: ioctl data* |
* @file_prive: DRM file info |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Set plane info, including placement, fb, scaling, and other factors. |
* Or pass a NULL fb to disable. |
*/ |
int drm_mode_setplane(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_set_plane *plane_req = data; |
struct drm_mode_object *obj; |
struct drm_plane *plane; |
struct drm_crtc *crtc; |
struct drm_framebuffer *fb; |
int ret = 0; |
unsigned int fb_width, fb_height; |
int i; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
mutex_lock(&dev->mode_config.mutex); |
|
/* |
* First, find the plane, crtc, and fb objects. If not available, |
* we don't bother to call the driver. |
*/ |
obj = drm_mode_object_find(dev, plane_req->plane_id, |
DRM_MODE_OBJECT_PLANE); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown plane ID %d\n", |
plane_req->plane_id); |
ret = -ENOENT; |
goto out; |
} |
plane = obj_to_plane(obj); |
|
/* No fb means shut it down */ |
if (!plane_req->fb_id) { |
plane->funcs->disable_plane(plane); |
plane->crtc = NULL; |
plane->fb = NULL; |
goto out; |
} |
|
obj = drm_mode_object_find(dev, plane_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown crtc ID %d\n", |
plane_req->crtc_id); |
ret = -ENOENT; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
|
obj = drm_mode_object_find(dev, plane_req->fb_id, |
DRM_MODE_OBJECT_FB); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", |
plane_req->fb_id); |
ret = -ENOENT; |
goto out; |
} |
fb = obj_to_fb(obj); |
|
/* Check whether this plane supports the fb pixel format. */ |
for (i = 0; i < plane->format_count; i++) |
if (fb->pixel_format == plane->format_types[i]) |
break; |
if (i == plane->format_count) { |
DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); |
ret = -EINVAL; |
goto out; |
} |
|
fb_width = fb->width << 16; |
fb_height = fb->height << 16; |
|
/* Make sure source coordinates are inside the fb. */ |
if (plane_req->src_w > fb_width || |
plane_req->src_x > fb_width - plane_req->src_w || |
plane_req->src_h > fb_height || |
plane_req->src_y > fb_height - plane_req->src_h) { |
DRM_DEBUG_KMS("Invalid source coordinates " |
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", |
plane_req->src_w >> 16, |
((plane_req->src_w & 0xffff) * 15625) >> 10, |
plane_req->src_h >> 16, |
((plane_req->src_h & 0xffff) * 15625) >> 10, |
plane_req->src_x >> 16, |
((plane_req->src_x & 0xffff) * 15625) >> 10, |
plane_req->src_y >> 16, |
((plane_req->src_y & 0xffff) * 15625) >> 10); |
ret = -ENOSPC; |
goto out; |
} |
|
/* Give drivers some help against integer overflows */ |
if (plane_req->crtc_w > INT_MAX || |
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || |
plane_req->crtc_h > INT_MAX || |
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { |
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", |
plane_req->crtc_w, plane_req->crtc_h, |
plane_req->crtc_x, plane_req->crtc_y); |
ret = -ERANGE; |
goto out; |
} |
|
ret = plane->funcs->update_plane(plane, crtc, fb, |
plane_req->crtc_x, plane_req->crtc_y, |
plane_req->crtc_w, plane_req->crtc_h, |
plane_req->src_x, plane_req->src_y, |
plane_req->src_w, plane_req->src_h); |
if (!ret) { |
plane->crtc = crtc; |
plane->fb = fb; |
} |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
|
return ret; |
} |
|
/** |
* drm_mode_setcrtc - set CRTC configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
1475,7 → 1788,7 |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* Takes mode config lock. |
* |
* Build a new CRTC configuration based on user request. |
* |
1490,18 → 1803,22 |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_mode_crtc *crtc_req = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc, *crtcfb; |
struct drm_crtc *crtc; |
struct drm_connector **connector_set = NULL, *connector; |
struct drm_framebuffer *fb = NULL; |
struct drm_display_mode *mode = NULL; |
struct drm_mode_set set; |
uint32_t __user *set_connectors_ptr; |
int ret = 0; |
int ret; |
int i; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
/* For some reason crtc x/y offsets are signed internally. */ |
if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) |
return -ERANGE; |
|
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, crtc_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
1514,17 → 1831,16 |
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); |
|
if (crtc_req->mode_valid) { |
int hdisplay, vdisplay; |
/* If we have a mode we need a framebuffer. */ |
/* If we pass -1, set the mode with the currently bound fb */ |
if (crtc_req->fb_id == -1) { |
list_for_each_entry(crtcfb, |
&dev->mode_config.crtc_list, head) { |
if (crtcfb == crtc) { |
DRM_DEBUG_KMS("Using current fb for " |
"setmode\n"); |
if (!crtc->fb) { |
DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); |
ret = -EINVAL; |
goto out; |
} |
fb = crtc->fb; |
} |
} |
} else { |
obj = drm_mode_object_find(dev, crtc_req->fb_id, |
DRM_MODE_OBJECT_FB); |
1538,9 → 1854,37 |
} |
|
mode = drm_mode_create(dev); |
drm_crtc_convert_umode(mode, &crtc_req->mode); |
if (!mode) { |
ret = -ENOMEM; |
goto out; |
} |
|
ret = drm_crtc_convert_umode(mode, &crtc_req->mode); |
if (ret) { |
DRM_DEBUG_KMS("Invalid mode\n"); |
goto out; |
} |
|
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
|
hdisplay = mode->hdisplay; |
vdisplay = mode->vdisplay; |
|
if (crtc->invert_dimensions) |
swap(hdisplay, vdisplay); |
|
if (hdisplay > fb->width || |
vdisplay > fb->height || |
crtc_req->x > fb->width - hdisplay || |
crtc_req->y > fb->height - vdisplay) { |
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", |
fb->width, fb->height, |
hdisplay, vdisplay, crtc_req->x, crtc_req->y, |
crtc->invert_dimensions ? " (inverted)" : ""); |
ret = -ENOSPC; |
goto out; |
} |
} |
|
if (crtc_req->count_connectors == 0 && mode) { |
DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); |
1573,7 → 1917,7 |
} |
|
for (i = 0; i < crtc_req->count_connectors; i++) { |
set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr; |
set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; |
if (get_user(out_id, &set_connectors_ptr[i])) { |
ret = -EFAULT; |
goto out; |
1607,6 → 1951,7 |
|
out: |
kfree(connector_set); |
drm_mode_destroy(dev, mode); |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
1622,10 → 1967,8 |
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
if (!req->flags) { |
DRM_ERROR("no operation set\n"); |
if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) |
return -EINVAL; |
} |
|
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); |
1638,7 → 1981,6 |
|
if (req->flags & DRM_MODE_CURSOR_BO) { |
if (!crtc->funcs->cursor_set) { |
DRM_ERROR("crtc does not support cursor\n"); |
ret = -ENXIO; |
goto out; |
} |
1651,7 → 1993,6 |
if (crtc->funcs->cursor_move) { |
ret = crtc->funcs->cursor_move(crtc, req->x, req->y); |
} else { |
DRM_ERROR("crtc does not support cursor\n"); |
ret = -EFAULT; |
goto out; |
} |
1660,7 → 2001,43 |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
#endif |
/* Original addfb only supported RGB formats, so figure out which one */ |
uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) |
{ |
uint32_t fmt; |
|
switch (bpp) { |
case 8: |
fmt = DRM_FORMAT_RGB332; |
break; |
case 16: |
if (depth == 15) |
fmt = DRM_FORMAT_XRGB1555; |
else |
fmt = DRM_FORMAT_RGB565; |
break; |
case 24: |
fmt = DRM_FORMAT_RGB888; |
break; |
case 32: |
if (depth == 24) |
fmt = DRM_FORMAT_XRGB8888; |
else if (depth == 30) |
fmt = DRM_FORMAT_XRGB2101010; |
else |
fmt = DRM_FORMAT_ARGB8888; |
break; |
default: |
DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); |
fmt = DRM_FORMAT_XRGB8888; |
break; |
} |
|
return fmt; |
} |
EXPORT_SYMBOL(drm_mode_legacy_fb_format); |
#if 0 |
/** |
* drm_mode_addfb - add an FB to the graphics configuration |
* @inode: inode from the ioctl |
1681,31 → 2058,210 |
int drm_mode_addfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_fb_cmd *r = data; |
struct drm_mode_fb_cmd *or = data; |
struct drm_mode_fb_cmd2 r = {}; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_framebuffer *fb; |
int ret = 0; |
|
/* Use new struct with format internally */ |
r.fb_id = or->fb_id; |
r.width = or->width; |
r.height = or->height; |
r.pitches[0] = or->pitch; |
r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); |
r.handles[0] = or->handle; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
if ((config->min_width > r.width) || (r.width > config->max_width)) |
return -EINVAL; |
|
if ((config->min_height > r.height) || (r.height > config->max_height)) |
return -EINVAL; |
|
mutex_lock(&dev->mode_config.mutex); |
|
/* TODO check buffer is sufficiently large */ |
/* TODO setup destructor callback */ |
|
fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); |
if (IS_ERR(fb)) { |
DRM_DEBUG_KMS("could not create framebuffer\n"); |
ret = PTR_ERR(fb); |
goto out; |
} |
|
or->fb_id = fb->base.id; |
list_add(&fb->filp_head, &file_priv->fbs); |
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
|
static int format_check(const struct drm_mode_fb_cmd2 *r) |
{ |
uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; |
|
switch (format) { |
case DRM_FORMAT_C8: |
case DRM_FORMAT_RGB332: |
case DRM_FORMAT_BGR233: |
case DRM_FORMAT_XRGB4444: |
case DRM_FORMAT_XBGR4444: |
case DRM_FORMAT_RGBX4444: |
case DRM_FORMAT_BGRX4444: |
case DRM_FORMAT_ARGB4444: |
case DRM_FORMAT_ABGR4444: |
case DRM_FORMAT_RGBA4444: |
case DRM_FORMAT_BGRA4444: |
case DRM_FORMAT_XRGB1555: |
case DRM_FORMAT_XBGR1555: |
case DRM_FORMAT_RGBX5551: |
case DRM_FORMAT_BGRX5551: |
case DRM_FORMAT_ARGB1555: |
case DRM_FORMAT_ABGR1555: |
case DRM_FORMAT_RGBA5551: |
case DRM_FORMAT_BGRA5551: |
case DRM_FORMAT_RGB565: |
case DRM_FORMAT_BGR565: |
case DRM_FORMAT_RGB888: |
case DRM_FORMAT_BGR888: |
case DRM_FORMAT_XRGB8888: |
case DRM_FORMAT_XBGR8888: |
case DRM_FORMAT_RGBX8888: |
case DRM_FORMAT_BGRX8888: |
case DRM_FORMAT_ARGB8888: |
case DRM_FORMAT_ABGR8888: |
case DRM_FORMAT_RGBA8888: |
case DRM_FORMAT_BGRA8888: |
case DRM_FORMAT_XRGB2101010: |
case DRM_FORMAT_XBGR2101010: |
case DRM_FORMAT_RGBX1010102: |
case DRM_FORMAT_BGRX1010102: |
case DRM_FORMAT_ARGB2101010: |
case DRM_FORMAT_ABGR2101010: |
case DRM_FORMAT_RGBA1010102: |
case DRM_FORMAT_BGRA1010102: |
case DRM_FORMAT_YUYV: |
case DRM_FORMAT_YVYU: |
case DRM_FORMAT_UYVY: |
case DRM_FORMAT_VYUY: |
case DRM_FORMAT_AYUV: |
case DRM_FORMAT_NV12: |
case DRM_FORMAT_NV21: |
case DRM_FORMAT_NV16: |
case DRM_FORMAT_NV61: |
case DRM_FORMAT_NV24: |
case DRM_FORMAT_NV42: |
case DRM_FORMAT_YUV410: |
case DRM_FORMAT_YVU410: |
case DRM_FORMAT_YUV411: |
case DRM_FORMAT_YVU411: |
case DRM_FORMAT_YUV420: |
case DRM_FORMAT_YVU420: |
case DRM_FORMAT_YUV422: |
case DRM_FORMAT_YVU422: |
case DRM_FORMAT_YUV444: |
case DRM_FORMAT_YVU444: |
return 0; |
default: |
return -EINVAL; |
} |
} |
|
static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) |
{ |
int ret, hsub, vsub, num_planes, i; |
|
ret = format_check(r); |
if (ret) { |
DRM_DEBUG_KMS("bad framebuffer format 0x%08x\n", r->pixel_format); |
return ret; |
} |
|
hsub = drm_format_horz_chroma_subsampling(r->pixel_format); |
vsub = drm_format_vert_chroma_subsampling(r->pixel_format); |
num_planes = drm_format_num_planes(r->pixel_format); |
|
if (r->width == 0 || r->width % hsub) { |
DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); |
return -EINVAL; |
} |
|
if (r->height == 0 || r->height % vsub) { |
DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); |
return -EINVAL; |
} |
|
for (i = 0; i < num_planes; i++) { |
unsigned int width = r->width / (i != 0 ? hsub : 1); |
|
if (!r->handles[i]) { |
DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); |
return -EINVAL; |
} |
|
if (r->pitches[i] < drm_format_plane_cpp(r->pixel_format, i) * width) { |
DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); |
return -EINVAL; |
} |
} |
|
return 0; |
} |
|
/** |
* drm_mode_addfb2 - add an FB to the graphics configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Add a new FB to the specified CRTC, given a user request with format. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_addfb2(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_fb_cmd2 *r = data; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_framebuffer *fb; |
int ret; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
if ((config->min_width > r->width) || (r->width > config->max_width)) { |
DRM_ERROR("mode new framebuffer width not within limits\n"); |
DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", |
r->width, config->min_width, config->max_width); |
return -EINVAL; |
} |
if ((config->min_height > r->height) || (r->height > config->max_height)) { |
DRM_ERROR("mode new framebuffer height not within limits\n"); |
DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", |
r->height, config->min_height, config->max_height); |
return -EINVAL; |
} |
|
ret = framebuffer_check(r); |
if (ret) |
return ret; |
|
mutex_lock(&dev->mode_config.mutex); |
|
/* TODO check buffer is sufficiently large */ |
/* TODO setup destructor callback */ |
|
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); |
if (IS_ERR(fb)) { |
DRM_ERROR("could not create framebuffer\n"); |
DRM_DEBUG_KMS("could not create framebuffer\n"); |
ret = PTR_ERR(fb); |
goto out; |
} |
1753,7 → 2309,6 |
obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); |
/* TODO check that we really get a framebuffer back. */ |
if (!obj) { |
DRM_ERROR("mode invalid framebuffer id\n"); |
ret = -EINVAL; |
goto out; |
} |
1764,17 → 2319,12 |
found = 1; |
|
if (!found) { |
DRM_ERROR("tried to remove a fb that we didn't own\n"); |
ret = -EINVAL; |
goto out; |
} |
|
/* TODO release all crtc connected to the framebuffer */ |
/* TODO unhock the destructor from the buffer object */ |
drm_framebuffer_remove(fb); |
|
list_del(&fb->filp_head); |
fb->funcs->destroy(fb); |
|
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
1788,7 → 2338,7 |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* Takes mode config lock. |
* |
* Lookup the FB given its ID and return info about it. |
* |
1811,7 → 2361,6 |
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; |
} |
1821,7 → 2370,7 |
r->width = fb->width; |
r->depth = fb->depth; |
r->bpp = fb->bits_per_pixel; |
r->pitch = fb->pitch; |
r->pitch = fb->pitches[0]; |
fb->funcs->create_handle(fb, file_priv, &r->handle); |
|
out: |
1839,7 → 2388,7 |
struct drm_framebuffer *fb; |
unsigned flags; |
int num_clips; |
int ret = 0; |
int ret; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
1847,7 → 2396,6 |
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; |
} |
1854,7 → 2402,7 |
fb = obj_to_fb(obj); |
|
num_clips = r->num_clips; |
clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr; |
clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; |
|
if (!num_clips != !clips_ptr) { |
ret = -EINVAL; |
1870,6 → 2418,10 |
} |
|
if (num_clips && clips_ptr) { |
if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { |
ret = -EINVAL; |
goto out_err1; |
} |
clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); |
if (!clips) { |
ret = -ENOMEM; |
1921,8 → 2473,7 |
|
mutex_lock(&dev->mode_config.mutex); |
list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { |
list_del(&fb->filp_head); |
fb->funcs->destroy(fb); |
drm_framebuffer_remove(fb); |
} |
mutex_unlock(&dev->mode_config.mutex); |
} |
1936,39 → 2487,49 |
* |
* Add @mode to @connector's user mode list. |
*/ |
static int drm_mode_attachmode(struct drm_device *dev, |
static void drm_mode_attachmode(struct drm_device *dev, |
struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
int ret = 0; |
|
list_add_tail(&mode->head, &connector->user_modes); |
return ret; |
} |
|
int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, |
struct drm_display_mode *mode) |
const struct drm_display_mode *mode) |
{ |
struct drm_connector *connector; |
int ret = 0; |
struct drm_display_mode *dup_mode; |
int need_dup = 0; |
struct drm_display_mode *dup_mode, *next; |
LIST_HEAD(list); |
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder) |
break; |
continue; |
if (connector->encoder->crtc == crtc) { |
if (need_dup) |
dup_mode = drm_mode_duplicate(dev, mode); |
else |
dup_mode = mode; |
ret = drm_mode_attachmode(dev, connector, dup_mode); |
if (ret) |
return ret; |
need_dup = 1; |
if (!dup_mode) { |
ret = -ENOMEM; |
goto out; |
} |
list_add_tail(&dup_mode->head, &list); |
} |
return 0; |
} |
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder) |
continue; |
if (connector->encoder->crtc == crtc) |
list_move_tail(list.next, &connector->user_modes); |
} |
|
WARN_ON(!list_empty(&list)); |
|
out: |
list_for_each_entry_safe(dup_mode, next, &list, head) |
drm_mode_destroy(dev, dup_mode); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_mode_attachmode_crtc); |
|
static int drm_mode_detachmode(struct drm_device *dev, |
2028,7 → 2589,7 |
struct drm_display_mode *mode; |
struct drm_mode_object *obj; |
struct drm_mode_modeinfo *umode = &mode_cmd->mode; |
int ret = 0; |
int ret; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
2048,9 → 2609,14 |
goto out; |
} |
|
drm_crtc_convert_umode(mode, umode); |
ret = drm_crtc_convert_umode(mode, umode); |
if (ret) { |
DRM_DEBUG_KMS("Invalid mode\n"); |
drm_mode_destroy(dev, mode); |
goto out; |
} |
|
ret = drm_mode_attachmode(dev, connector, mode); |
drm_mode_attachmode(dev, connector, mode); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
2077,7 → 2643,7 |
struct drm_connector *connector; |
struct drm_display_mode mode; |
struct drm_mode_modeinfo *umode = &mode_cmd->mode; |
int ret = 0; |
int ret; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
2091,7 → 2657,12 |
} |
connector = obj_to_connector(obj); |
|
drm_crtc_convert_umode(&mode, umode); |
ret = drm_crtc_convert_umode(&mode, umode); |
if (ret) { |
DRM_DEBUG_KMS("Invalid mode\n"); |
goto out; |
} |
|
ret = drm_mode_detachmode(dev, connector, &mode); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
2103,6 → 2674,7 |
const char *name, int num_values) |
{ |
struct drm_property *property = NULL; |
int ret; |
|
property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); |
if (!property) |
2114,30 → 2686,118 |
goto fail; |
} |
|
drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); |
ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); |
if (ret) |
goto fail; |
|
property->flags = flags; |
property->num_values = num_values; |
INIT_LIST_HEAD(&property->enum_blob_list); |
|
if (name) |
if (name) { |
strncpy(property->name, name, DRM_PROP_NAME_LEN); |
property->name[DRM_PROP_NAME_LEN-1] = '\0'; |
} |
|
list_add_tail(&property->head, &dev->mode_config.property_list); |
return property; |
fail: |
kfree(property->values); |
kfree(property); |
return NULL; |
} |
EXPORT_SYMBOL(drm_property_create); |
|
struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, |
const char *name, |
const struct drm_prop_enum_list *props, |
int num_values) |
{ |
struct drm_property *property; |
int i, ret; |
|
flags |= DRM_MODE_PROP_ENUM; |
|
property = drm_property_create(dev, flags, name, num_values); |
if (!property) |
return NULL; |
|
for (i = 0; i < num_values; i++) { |
ret = drm_property_add_enum(property, i, |
props[i].type, |
props[i].name); |
if (ret) { |
drm_property_destroy(dev, property); |
return NULL; |
} |
} |
|
return property; |
} |
EXPORT_SYMBOL(drm_property_create_enum); |
|
struct drm_property *drm_property_create_bitmask(struct drm_device *dev, |
int flags, const char *name, |
const struct drm_prop_enum_list *props, |
int num_values) |
{ |
struct drm_property *property; |
int i, ret; |
|
flags |= DRM_MODE_PROP_BITMASK; |
|
property = drm_property_create(dev, flags, name, num_values); |
if (!property) |
return NULL; |
|
for (i = 0; i < num_values; i++) { |
ret = drm_property_add_enum(property, i, |
props[i].type, |
props[i].name); |
if (ret) { |
drm_property_destroy(dev, property); |
return NULL; |
} |
} |
|
return property; |
} |
EXPORT_SYMBOL(drm_property_create_bitmask); |
|
struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, |
const char *name, |
uint64_t min, uint64_t max) |
{ |
struct drm_property *property; |
|
flags |= DRM_MODE_PROP_RANGE; |
|
property = drm_property_create(dev, flags, name, 2); |
if (!property) |
return NULL; |
|
property->values[0] = min; |
property->values[1] = max; |
|
return property; |
} |
EXPORT_SYMBOL(drm_property_create_range); |
|
int drm_property_add_enum(struct drm_property *property, int index, |
uint64_t value, const char *name) |
{ |
struct drm_property_enum *prop_enum; |
|
if (!(property->flags & DRM_MODE_PROP_ENUM)) |
if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) |
return -EINVAL; |
|
/* |
* Bitmask enum properties have the additional constraint of values |
* from 0 to 63 |
*/ |
if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) |
return -EINVAL; |
|
if (!list_empty(&property->enum_blob_list)) { |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) { |
if (prop_enum->value == value) { |
2179,60 → 2839,78 |
} |
EXPORT_SYMBOL(drm_property_destroy); |
|
int drm_connector_attach_property(struct drm_connector *connector, |
void drm_connector_attach_property(struct drm_connector *connector, |
struct drm_property *property, uint64_t init_val) |
{ |
int i; |
drm_object_attach_property(&connector->base, property, init_val); |
} |
EXPORT_SYMBOL(drm_connector_attach_property); |
|
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == 0) { |
connector->property_ids[i] = property->base.id; |
connector->property_values[i] = init_val; |
break; |
int drm_connector_property_set_value(struct drm_connector *connector, |
struct drm_property *property, uint64_t value) |
{ |
return drm_object_property_set_value(&connector->base, property, value); |
} |
EXPORT_SYMBOL(drm_connector_property_set_value); |
|
int drm_connector_property_get_value(struct drm_connector *connector, |
struct drm_property *property, uint64_t *val) |
{ |
return drm_object_property_get_value(&connector->base, property, val); |
} |
EXPORT_SYMBOL(drm_connector_property_get_value); |
|
if (i == DRM_CONNECTOR_MAX_PROPERTY) |
return -EINVAL; |
return 0; |
void drm_object_attach_property(struct drm_mode_object *obj, |
struct drm_property *property, |
uint64_t init_val) |
{ |
int count = obj->properties->count; |
|
if (count == DRM_OBJECT_MAX_PROPERTY) { |
WARN(1, "Failed to attach object property (type: 0x%x). Please " |
"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " |
"you see this message on the same object type.\n", |
obj->type); |
return; |
} |
EXPORT_SYMBOL(drm_connector_attach_property); |
|
int drm_connector_property_set_value(struct drm_connector *connector, |
struct drm_property *property, uint64_t value) |
obj->properties->ids[count] = property->base.id; |
obj->properties->values[count] = init_val; |
obj->properties->count++; |
} |
EXPORT_SYMBOL(drm_object_attach_property); |
|
int drm_object_property_set_value(struct drm_mode_object *obj, |
struct drm_property *property, uint64_t val) |
{ |
int i; |
|
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == property->base.id) { |
connector->property_values[i] = value; |
break; |
for (i = 0; i < obj->properties->count; i++) { |
if (obj->properties->ids[i] == property->base.id) { |
obj->properties->values[i] = val; |
return 0; |
} |
} |
|
if (i == DRM_CONNECTOR_MAX_PROPERTY) |
return -EINVAL; |
return 0; |
} |
EXPORT_SYMBOL(drm_connector_property_set_value); |
EXPORT_SYMBOL(drm_object_property_set_value); |
|
int drm_connector_property_get_value(struct drm_connector *connector, |
int drm_object_property_get_value(struct drm_mode_object *obj, |
struct drm_property *property, uint64_t *val) |
{ |
int i; |
|
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == property->base.id) { |
*val = connector->property_values[i]; |
break; |
for (i = 0; i < obj->properties->count; i++) { |
if (obj->properties->ids[i] == property->base.id) { |
*val = obj->properties->values[i]; |
return 0; |
} |
} |
|
if (i == DRM_CONNECTOR_MAX_PROPERTY) |
return -EINVAL; |
return 0; |
} |
EXPORT_SYMBOL(drm_connector_property_get_value); |
EXPORT_SYMBOL(drm_object_property_get_value); |
|
#if 0 |
int drm_mode_getproperty_ioctl(struct drm_device *dev, |
2249,7 → 2927,7 |
struct drm_property_enum *prop_enum; |
struct drm_mode_property_enum __user *enum_ptr; |
struct drm_property_blob *prop_blob; |
uint32_t *blob_id_ptr; |
uint32_t __user *blob_id_ptr; |
uint64_t __user *values_ptr; |
uint32_t __user *blob_length_ptr; |
|
2264,7 → 2942,7 |
} |
property = obj_to_property(obj); |
|
if (property->flags & DRM_MODE_PROP_ENUM) { |
if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) |
enum_count++; |
} else if (property->flags & DRM_MODE_PROP_BLOB) { |
2279,7 → 2957,7 |
out_resp->flags = property->flags; |
|
if ((out_resp->count_values >= value_count) && value_count) { |
values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr; |
values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; |
for (i = 0; i < value_count; i++) { |
if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { |
ret = -EFAULT; |
2289,10 → 2967,10 |
} |
out_resp->count_values = value_count; |
|
if (property->flags & DRM_MODE_PROP_ENUM) { |
if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { |
if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { |
copied = 0; |
enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr; |
enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) { |
|
if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { |
2314,8 → 2992,8 |
if (property->flags & DRM_MODE_PROP_BLOB) { |
if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { |
copied = 0; |
blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr; |
blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr; |
blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; |
blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; |
|
list_for_each_entry(prop_blob, &property->enum_blob_list, head) { |
if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { |
2343,6 → 3021,7 |
void *data) |
{ |
struct drm_property_blob *blob; |
int ret; |
|
if (!length || !data) |
return NULL; |
2351,13 → 3030,16 |
if (!blob) |
return NULL; |
|
blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob)); |
ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); |
if (ret) { |
kfree(blob); |
return NULL; |
} |
|
blob->length = length; |
|
memcpy(blob->data, data, length); |
|
drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); |
|
list_add_tail(&blob->head, &dev->mode_config.property_blob_list); |
return blob; |
} |
2378,7 → 3060,7 |
struct drm_mode_get_blob *out_resp = data; |
struct drm_property_blob *blob; |
int ret = 0; |
void *blob_ptr; |
void __user *blob_ptr; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
2392,7 → 3074,7 |
blob = obj_to_blob(obj); |
|
if (out_resp->length == blob->length) { |
blob_ptr = (void *)(unsigned long)out_resp->data; |
blob_ptr = (void __user *)(unsigned long)out_resp->data; |
if (copy_to_user(blob_ptr, blob->data, blob->length)){ |
ret = -EFAULT; |
goto done; |
2410,7 → 3092,7 |
struct edid *edid) |
{ |
struct drm_device *dev = connector->dev; |
int ret = 0, size; |
int ret, size; |
|
if (connector->edid_blob_ptr) |
drm_property_destroy_blob(dev, connector->edid_blob_ptr); |
2435,15 → 3117,69 |
EXPORT_SYMBOL(drm_mode_connector_update_edid_property); |
|
#if 0 |
int drm_mode_connector_property_set_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
|
static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, |
struct drm_property *property, |
uint64_t value) |
{ |
struct drm_mode_connector_set_property *out_resp = data; |
int ret = -EINVAL; |
struct drm_connector *connector = obj_to_connector(obj); |
|
/* Do DPMS ourselves */ |
if (property == connector->dev->mode_config.dpms_property) { |
if (connector->funcs->dpms) |
(*connector->funcs->dpms)(connector, (int)value); |
ret = 0; |
} else if (connector->funcs->set_property) |
ret = connector->funcs->set_property(connector, property, value); |
|
/* store the property value if successful */ |
if (!ret) |
drm_connector_property_set_value(connector, property, value); |
return ret; |
} |
|
static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, |
struct drm_property *property, |
uint64_t value) |
{ |
int ret = -EINVAL; |
struct drm_crtc *crtc = obj_to_crtc(obj); |
|
if (crtc->funcs->set_property) |
ret = crtc->funcs->set_property(crtc, property, value); |
if (!ret) |
drm_object_property_set_value(obj, property, value); |
|
return ret; |
} |
|
static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, |
struct drm_property *property, |
uint64_t value) |
{ |
int ret = -EINVAL; |
struct drm_plane *plane = obj_to_plane(obj); |
|
if (plane->funcs->set_property) |
ret = plane->funcs->set_property(plane, property, value); |
if (!ret) |
drm_object_property_set_value(obj, property, value); |
|
return ret; |
} |
|
int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_obj_get_properties *arg = data; |
struct drm_mode_object *obj; |
struct drm_property *property; |
struct drm_connector *connector; |
int ret = -EINVAL; |
int ret = 0; |
int i; |
int copied = 0; |
int props_count = 0; |
uint32_t __user *props_ptr; |
uint64_t __user *prop_values_ptr; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
2450,60 → 3186,95 |
|
mutex_lock(&dev->mode_config.mutex); |
|
obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); |
obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
connector = obj_to_connector(obj); |
|
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == out_resp->prop_id) |
break; |
if (!obj->properties) { |
ret = -EINVAL; |
goto out; |
} |
|
if (i == DRM_CONNECTOR_MAX_PROPERTY) { |
props_count = obj->properties->count; |
|
/* This ioctl is called twice, once to determine how much space is |
* needed, and the 2nd time to fill it. */ |
if ((arg->count_props >= props_count) && props_count) { |
copied = 0; |
props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); |
prop_values_ptr = (uint64_t __user *)(unsigned long) |
(arg->prop_values_ptr); |
for (i = 0; i < props_count; i++) { |
if (put_user(obj->properties->ids[i], |
props_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
|
obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); |
if (!obj) { |
if (put_user(obj->properties->values[i], |
prop_values_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
property = obj_to_property(obj); |
copied++; |
} |
} |
arg->count_props = props_count; |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
|
if (property->flags & DRM_MODE_PROP_IMMUTABLE) |
int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_obj_set_property *arg = data; |
struct drm_mode_object *arg_obj; |
struct drm_mode_object *prop_obj; |
struct drm_property *property; |
int ret = -EINVAL; |
int i; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
mutex_lock(&dev->mode_config.mutex); |
|
arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); |
if (!arg_obj) |
goto out; |
if (!arg_obj->properties) |
goto out; |
|
if (property->flags & DRM_MODE_PROP_RANGE) { |
if (out_resp->value < property->values[0]) |
for (i = 0; i < arg_obj->properties->count; i++) |
if (arg_obj->properties->ids[i] == arg->prop_id) |
break; |
|
if (i == arg_obj->properties->count) |
goto out; |
|
if (out_resp->value > property->values[1]) |
prop_obj = drm_mode_object_find(dev, arg->prop_id, |
DRM_MODE_OBJECT_PROPERTY); |
if (!prop_obj) |
goto out; |
} else { |
int found = 0; |
for (i = 0; i < property->num_values; i++) { |
if (property->values[i] == out_resp->value) { |
found = 1; |
property = obj_to_property(prop_obj); |
|
if (!drm_property_change_is_valid(property, arg->value)) |
goto out; |
|
switch (arg_obj->type) { |
case DRM_MODE_OBJECT_CONNECTOR: |
ret = drm_mode_connector_set_obj_prop(arg_obj, property, |
arg->value); |
break; |
case DRM_MODE_OBJECT_CRTC: |
ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); |
break; |
case DRM_MODE_OBJECT_PLANE: |
ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); |
break; |
} |
} |
if (!found) { |
goto out; |
} |
} |
|
/* Do DPMS ourselves */ |
if (property == connector->dev->mode_config.dpms_property) { |
if (connector->funcs->dpms) |
(*connector->funcs->dpms)(connector, (int) out_resp->value); |
ret = 0; |
} else if (connector->funcs->set_property) |
ret = connector->funcs->set_property(connector, property, out_resp->value); |
|
/* store the property value if successful */ |
if (!ret) |
drm_connector_property_set_value(connector, property, out_resp->value); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
2540,7 → 3311,7 |
} |
EXPORT_SYMBOL(drm_mode_connector_detach_encoder); |
|
bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, |
int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, |
int gamma_size) |
{ |
crtc->gamma_size = gamma_size; |
2548,10 → 3319,10 |
crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); |
if (!crtc->gamma_store) { |
crtc->gamma_size = 0; |
return false; |
return -ENOMEM; |
} |
|
return true; |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); |
|
2577,6 → 3348,11 |
} |
crtc = obj_to_crtc(obj); |
|
if (crtc->funcs->gamma_set == NULL) { |
ret = -ENOSYS; |
goto out; |
} |
|
/* memcpy into gamma store */ |
if (crtc_lut->gamma_size != crtc->gamma_size) { |
ret = -EINVAL; |
2682,3 → 3458,211 |
connector->funcs->reset(connector); |
} |
EXPORT_SYMBOL(drm_mode_config_reset); |
/* |
* Just need to support RGB formats here for compat with code that doesn't |
* use pixel formats directly yet. |
*/ |
void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, |
int *bpp) |
{ |
switch (format) { |
case DRM_FORMAT_RGB332: |
case DRM_FORMAT_BGR233: |
*depth = 8; |
*bpp = 8; |
break; |
case DRM_FORMAT_XRGB1555: |
case DRM_FORMAT_XBGR1555: |
case DRM_FORMAT_RGBX5551: |
case DRM_FORMAT_BGRX5551: |
case DRM_FORMAT_ARGB1555: |
case DRM_FORMAT_ABGR1555: |
case DRM_FORMAT_RGBA5551: |
case DRM_FORMAT_BGRA5551: |
*depth = 15; |
*bpp = 16; |
break; |
case DRM_FORMAT_RGB565: |
case DRM_FORMAT_BGR565: |
*depth = 16; |
*bpp = 16; |
break; |
case DRM_FORMAT_RGB888: |
case DRM_FORMAT_BGR888: |
*depth = 24; |
*bpp = 24; |
break; |
case DRM_FORMAT_XRGB8888: |
case DRM_FORMAT_XBGR8888: |
case DRM_FORMAT_RGBX8888: |
case DRM_FORMAT_BGRX8888: |
*depth = 24; |
*bpp = 32; |
break; |
case DRM_FORMAT_XRGB2101010: |
case DRM_FORMAT_XBGR2101010: |
case DRM_FORMAT_RGBX1010102: |
case DRM_FORMAT_BGRX1010102: |
case DRM_FORMAT_ARGB2101010: |
case DRM_FORMAT_ABGR2101010: |
case DRM_FORMAT_RGBA1010102: |
case DRM_FORMAT_BGRA1010102: |
*depth = 30; |
*bpp = 32; |
break; |
case DRM_FORMAT_ARGB8888: |
case DRM_FORMAT_ABGR8888: |
case DRM_FORMAT_RGBA8888: |
case DRM_FORMAT_BGRA8888: |
*depth = 32; |
*bpp = 32; |
break; |
default: |
DRM_DEBUG_KMS("unsupported pixel format\n"); |
*depth = 0; |
*bpp = 0; |
break; |
} |
} |
EXPORT_SYMBOL(drm_fb_get_bpp_depth); |
|
/** |
* drm_format_num_planes - get the number of planes for format |
* @format: pixel format (DRM_FORMAT_*) |
* |
* RETURNS: |
* The number of planes used by the specified pixel format. |
*/ |
int drm_format_num_planes(uint32_t format) |
{ |
switch (format) { |
case DRM_FORMAT_YUV410: |
case DRM_FORMAT_YVU410: |
case DRM_FORMAT_YUV411: |
case DRM_FORMAT_YVU411: |
case DRM_FORMAT_YUV420: |
case DRM_FORMAT_YVU420: |
case DRM_FORMAT_YUV422: |
case DRM_FORMAT_YVU422: |
case DRM_FORMAT_YUV444: |
case DRM_FORMAT_YVU444: |
return 3; |
case DRM_FORMAT_NV12: |
case DRM_FORMAT_NV21: |
case DRM_FORMAT_NV16: |
case DRM_FORMAT_NV61: |
case DRM_FORMAT_NV24: |
case DRM_FORMAT_NV42: |
return 2; |
default: |
return 1; |
} |
} |
EXPORT_SYMBOL(drm_format_num_planes); |
|
/** |
* drm_format_plane_cpp - determine the bytes per pixel value |
* @format: pixel format (DRM_FORMAT_*) |
* @plane: plane index |
* |
* RETURNS: |
* The bytes per pixel value for the specified plane. |
*/ |
int drm_format_plane_cpp(uint32_t format, int plane) |
{ |
unsigned int depth; |
int bpp; |
|
if (plane >= drm_format_num_planes(format)) |
return 0; |
|
switch (format) { |
case DRM_FORMAT_YUYV: |
case DRM_FORMAT_YVYU: |
case DRM_FORMAT_UYVY: |
case DRM_FORMAT_VYUY: |
return 2; |
case DRM_FORMAT_NV12: |
case DRM_FORMAT_NV21: |
case DRM_FORMAT_NV16: |
case DRM_FORMAT_NV61: |
case DRM_FORMAT_NV24: |
case DRM_FORMAT_NV42: |
return plane ? 2 : 1; |
case DRM_FORMAT_YUV410: |
case DRM_FORMAT_YVU410: |
case DRM_FORMAT_YUV411: |
case DRM_FORMAT_YVU411: |
case DRM_FORMAT_YUV420: |
case DRM_FORMAT_YVU420: |
case DRM_FORMAT_YUV422: |
case DRM_FORMAT_YVU422: |
case DRM_FORMAT_YUV444: |
case DRM_FORMAT_YVU444: |
return 1; |
default: |
drm_fb_get_bpp_depth(format, &depth, &bpp); |
return bpp >> 3; |
} |
} |
EXPORT_SYMBOL(drm_format_plane_cpp); |
|
/** |
* drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor |
* @format: pixel format (DRM_FORMAT_*) |
* |
* RETURNS: |
* The horizontal chroma subsampling factor for the |
* specified pixel format. |
*/ |
int drm_format_horz_chroma_subsampling(uint32_t format) |
{ |
switch (format) { |
case DRM_FORMAT_YUV411: |
case DRM_FORMAT_YVU411: |
case DRM_FORMAT_YUV410: |
case DRM_FORMAT_YVU410: |
return 4; |
case DRM_FORMAT_YUYV: |
case DRM_FORMAT_YVYU: |
case DRM_FORMAT_UYVY: |
case DRM_FORMAT_VYUY: |
case DRM_FORMAT_NV12: |
case DRM_FORMAT_NV21: |
case DRM_FORMAT_NV16: |
case DRM_FORMAT_NV61: |
case DRM_FORMAT_YUV422: |
case DRM_FORMAT_YVU422: |
case DRM_FORMAT_YUV420: |
case DRM_FORMAT_YVU420: |
return 2; |
default: |
return 1; |
} |
} |
EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); |
|
/** |
* drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor |
* @format: pixel format (DRM_FORMAT_*) |
* |
* RETURNS: |
* The vertical chroma subsampling factor for the |
* specified pixel format. |
*/ |
int drm_format_vert_chroma_subsampling(uint32_t format) |
{ |
switch (format) { |
case DRM_FORMAT_YUV410: |
case DRM_FORMAT_YVU410: |
return 4; |
case DRM_FORMAT_YUV420: |
case DRM_FORMAT_YVU420: |
case DRM_FORMAT_NV12: |
case DRM_FORMAT_NV21: |
return 2; |
default: |
return 1; |
} |
} |
EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); |