diff options
| -rw-r--r-- | linux-core/drmP.h | 4 | ||||
| -rw-r--r-- | linux-core/drm_crtc.c | 46 | ||||
| -rw-r--r-- | linux-core/drm_crtc.h | 6 | ||||
| -rw-r--r-- | linux-core/drm_sysfs.c | 197 | ||||
| -rw-r--r-- | linux-core/intel_display.c | 5 | ||||
| -rw-r--r-- | linux-core/intel_drv.h | 1 | 
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); | 
