summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-core/drmP.h4
-rw-r--r--linux-core/drm_crtc.c46
-rw-r--r--linux-core/drm_crtc.h6
-rw-r--r--linux-core/drm_sysfs.c197
-rw-r--r--linux-core/intel_display.c5
-rw-r--r--linux-core/intel_drv.h1
6 files changed, 249 insertions, 10 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index f96e6bee..24f8c3d8 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -1311,7 +1311,11 @@ struct drm_sysfs_class;
extern struct class *drm_sysfs_create(struct module *owner, char *name);
extern void drm_sysfs_destroy(void);
extern int drm_sysfs_device_add(struct drm_minor *minor);
+extern void drm_sysfs_hotplug_event(struct drm_device *dev);
extern void drm_sysfs_device_remove(struct drm_minor *minor);
+extern char *drm_get_output_status_name(enum drm_output_status status);
+extern int drm_sysfs_output_add(struct drm_output *output);
+extern void drm_sysfs_output_remove(struct drm_output *output);
/*
* Basic memory manager support (drm_mm.c)
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
index 0cd0ebd0..19155e1f 100644
--- a/linux-core/drm_crtc.c
+++ b/linux-core/drm_crtc.c
@@ -47,6 +47,18 @@ static struct drm_prop_enum_list drm_dpms_enum_list[] =
{ DPMSModeSuspend, "Suspend" },
{ DPMSModeOff, "Off" }
};
+
+char *drm_get_dpms_name(int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+ if (drm_dpms_enum_list[i].type == val)
+ return drm_dpms_enum_list[i].name;
+
+ return "unknown";
+}
+
static struct drm_prop_enum_list drm_conn_enum_list[] =
{ { ConnectorUnknown, "Unknown" },
{ ConnectorVGA, "VGA" },
@@ -79,6 +91,16 @@ char *drm_get_output_name(struct drm_output *output)
return buf;
}
+char *drm_get_output_status_name(enum drm_output_status status)
+{
+ if (status == output_status_connected)
+ return "connected";
+ else if (status == output_status_disconnected)
+ return "disconnected";
+ else
+ return "unknown";
+}
+
/**
* drm_idr_get - allocate a new identifier
* @dev: DRM device
@@ -629,6 +651,8 @@ struct drm_output *drm_output_create(struct drm_device *dev,
/* output_set_monitor(output)? */
/* check for output_ignored(output)? */
+ drm_sysfs_output_add(output);
+
mutex_lock(&dev->mode_config.mutex);
list_add_tail(&output->head, &dev->mode_config.output_list);
dev->mode_config.num_output++;
@@ -659,6 +683,8 @@ void drm_output_destroy(struct drm_output *output)
struct drm_device *dev = output->dev;
struct drm_display_mode *mode, *t;
+ drm_sysfs_output_remove(output);
+
if (*output->funcs->cleanup)
(*output->funcs->cleanup)(output);
@@ -1226,6 +1252,8 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
DRM_ERROR("failed to set mode after hotplug\n");
}
+ drm_sysfs_hotplug_event(dev);
+
drm_disable_unused_functions(dev);
return 0;
@@ -2223,6 +2251,24 @@ int drm_output_property_set_value(struct drm_output *output,
}
EXPORT_SYMBOL(drm_output_property_set_value);
+int drm_output_property_get_value(struct drm_output *output,
+ struct drm_property *property, uint64_t *val)
+{
+ int i;
+
+ for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+ if (output->property_ids[i] == property->id) {
+ *val = output->property_values[i];
+ break;
+ }
+ }
+
+ if (i == DRM_OUTPUT_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_output_property_get_value);
+
int drm_mode_getproperty_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
index bbeab603..ac0d2d5a 100644
--- a/linux-core/drm_crtc.h
+++ b/linux-core/drm_crtc.h
@@ -451,6 +451,8 @@ struct drm_output_funcs {
*/
struct drm_output {
struct drm_device *dev;
+ struct device kdev;
+ struct device_attribute *attr;
struct list_head head;
struct drm_crtc *crtc;
int id; /* idr assigned */
@@ -563,6 +565,7 @@ struct drm_output *drm_output_create(struct drm_device *dev,
int type);
extern char *drm_get_output_name(struct drm_output *output);
+extern char *drm_get_dpms_name(int val);
extern void drm_output_destroy(struct drm_output *output);
extern void drm_fb_release(struct file *filp);
@@ -606,6 +609,9 @@ extern int drm_mode_output_update_edid_property(struct drm_output *output,
extern int drm_output_property_set_value(struct drm_output *output,
struct drm_property *property,
uint64_t value);
+extern int drm_output_property_get_value(struct drm_output *output,
+ struct drm_property *property,
+ uint64_t *value);
extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev);
extern bool drm_initial_config(struct drm_device *dev, bool cangrow);
extern void drm_framebuffer_set_object(struct drm_device *dev,
diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c
index 3372a713..3e682c99 100644
--- a/linux-core/drm_sysfs.c
+++ b/linux-core/drm_sysfs.c
@@ -133,10 +133,6 @@ static ssize_t show_dri(struct device *device, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name);
}
-static struct device_attribute device_attrs[] = {
- __ATTR(dri_library_name, S_IRUGO, show_dri, NULL),
-};
-
/**
* drm_sysfs_device_release - do nothing
* @dev: Linux device
@@ -150,6 +146,189 @@ static void drm_sysfs_device_release(struct device *dev)
return;
}
+/*
+ * Output properties
+ */
+static ssize_t status_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_output *output = container_of(device, struct drm_output, kdev);
+ return snprintf(buf, PAGE_SIZE, "%s",
+ drm_get_output_status_name(output->funcs->detect(output)));
+}
+
+static ssize_t dpms_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_output *output = container_of(device, struct drm_output, kdev);
+ struct drm_device *dev = output->dev;
+ uint64_t dpms_status;
+ int ret;
+
+ ret = drm_output_property_get_value(output,
+ dev->mode_config.dpms_property,
+ &dpms_status);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", drm_get_dpms_name((int)dpms_status));
+}
+
+static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *output_dev = container_of(kobj, struct device, kobj);
+ struct drm_output *output = container_of(output_dev, struct drm_output,
+ kdev);
+ unsigned char *edid;
+ size_t size;
+
+ if (!output->edid_blob_ptr)
+ return 0;
+
+ edid = output->edid_blob_ptr->data;
+ size = output->edid_blob_ptr->length;
+ if (!edid)
+ return 0;
+
+ if (off >= size)
+ return 0;
+
+ if (off + count > size)
+ count = size - off;
+ memcpy(buf, edid + off, count);
+
+ return count;
+}
+
+static ssize_t modes_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_output *output = container_of(device, struct drm_output, kdev);
+ struct drm_display_mode *mode;
+ int written = 0;
+
+ list_for_each_entry(mode, &output->modes, head) {
+ written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
+ mode->name);
+ }
+
+ return written;
+}
+
+static struct device_attribute output_attrs[] = {
+ __ATTR_RO(status),
+ __ATTR_RO(dpms),
+ __ATTR_RO(modes),
+};
+
+static struct bin_attribute edid_attr = {
+ .attr.name = "edid",
+ .size = 128,
+ .read = edid_show,
+};
+
+/**
+ * drm_sysfs_output_add - add an output to sysfs
+ * @output: output to add
+ *
+ * Create an output device in sysfs, along with its associated output
+ * properties (so far, connection status, dpms, mode list & edid) and
+ * generate a hotplug event so userspace knows there's a new output
+ * available.
+ */
+int drm_sysfs_output_add(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ int ret = 0, i, j;
+
+ if (device_is_registered(&output->kdev))
+ return 0;
+
+ output->kdev.parent = &dev->primary->kdev;
+ output->kdev.class = drm_class;
+ output->kdev.release = drm_sysfs_device_release;
+
+ DRM_DEBUG("adding \"%s\" to sysfs", drm_get_output_name(output));
+
+ snprintf(output->kdev.bus_id, BUS_ID_SIZE, "card%d-%s",
+ dev->primary->index, drm_get_output_name(output));
+ ret = device_register(&output->kdev);
+
+ if (ret) {
+ DRM_ERROR("failed to register output device: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(output_attrs); i++) {
+ ret = device_create_file(&output->kdev, &output_attrs[i]);
+ if (ret)
+ goto err_out_files;
+ }
+
+ ret = sysfs_create_bin_file(&output->kdev.kobj, &edid_attr);
+ if (ret)
+ goto err_out_files;
+
+ /* Let userspace know we have a new output */
+ drm_sysfs_hotplug_event(dev);
+
+ return 0;
+
+err_out_files:
+ if (i > 0)
+ for (j = 0; j < i; j++)
+ device_remove_file(&output->kdev, &output_attrs[i]);
+ device_unregister(&output->kdev);
+
+out:
+ return ret;
+}
+
+/**
+ * drm_sysfs_output_remove - remove an output device from sysfs
+ * @output: output to remove
+ *
+ * Remove @output and its associated attributes from sysfs. Note that
+ * the device model core will take care of sending the "remove" uevent
+ * at this time, so we don't need to do it.
+ */
+void drm_sysfs_output_remove(struct drm_output *output)
+{
+ int i;
+
+ DRM_DEBUG("removing \"%s\" from sysfs\n", drm_get_output_name(output));
+ for (i = 0; i < i; i++)
+ device_remove_file(&output->kdev, &output_attrs[i]);
+ sysfs_remove_bin_file(&output->kdev.kobj, &edid_attr);
+ device_unregister(&output->kdev);
+}
+
+/**
+ * drm_sysfs_hotplug_event - generate a DRM uevent
+ * @dev: DRM device
+ *
+ * Send a uevent for the DRM device specified by @dev. Currently we only
+ * set HOTPLUG=1 in the uevent environment, but this could be expanded to
+ * deal with other types of events.
+ */
+void drm_sysfs_hotplug_event(struct drm_device *dev)
+{
+ char *event_string = "HOTPLUG=1";
+ char *envp[] = { event_string, NULL };
+
+ DRM_DEBUG("generating hotplug event\n");
+
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+}
+
+static struct device_attribute dri_attrs[] = {
+ __ATTR(dri_library_name, S_IRUGO, show_dri, NULL),
+};
+
/**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver
* @dev: DRM device to be added
@@ -184,8 +363,8 @@ int drm_sysfs_device_add(struct drm_minor *minor)
goto err_out;
}
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- err = device_create_file(&minor->kdev, &device_attrs[i]);
+ for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) {
+ err = device_create_file(&minor->kdev, &dri_attrs[i]);
if (err)
goto err_out_files;
}
@@ -195,7 +374,7 @@ int drm_sysfs_device_add(struct drm_minor *minor)
err_out_files:
if (i > 0)
for (j = 0; j < i; j++)
- device_remove_file(&minor->kdev, &device_attrs[i]);
+ device_remove_file(&minor->kdev, &dri_attrs[i]);
device_unregister(&minor->kdev);
err_out:
@@ -213,7 +392,7 @@ void drm_sysfs_device_remove(struct drm_minor *minor)
{
int i;
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
- device_remove_file(&minor->kdev, &device_attrs[i]);
+ for (i = 0; i < ARRAY_SIZE(dri_attrs); i++)
+ device_remove_file(&minor->kdev, &dri_attrs[i]);
device_unregister(&minor->kdev);
}
diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c
index fa2b9bea..0615c1c4 100644
--- a/linux-core/intel_display.c
+++ b/linux-core/intel_display.c
@@ -1292,7 +1292,10 @@ static void intel_setup_outputs(struct drm_device *dev)
intel_sdvo_init(dev, SDVOB);
intel_sdvo_init(dev, SDVOC);
}
-
+#if 0
+ if (IS_I9XX(dev) && !IS_I915G(dev))
+ intel_tv_init(dev);
+#endif
list_for_each_entry(output, &dev->mode_config.output_list, head) {
struct intel_output *intel_output = output->driver_private;
int crtc_mask = 0, clone_mask = 0;
diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h
index a36fd3f1..62e21a5e 100644
--- a/linux-core/intel_drv.h
+++ b/linux-core/intel_drv.h
@@ -68,6 +68,7 @@ extern bool intel_ddc_probe(struct drm_output *output);
extern void intel_crt_init(struct drm_device *dev);
extern void intel_sdvo_init(struct drm_device *dev, int output_device);
+extern void intel_tv_init(struct drm_device *dev);
extern void intel_lvds_init(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);