This adds an init-time configuration framework that parses configuration data from an ACPI property table. The table is assumed to have well defined sub-device property tables that correspond to the various driver components. Initially the following sub-device tables are defined: CRTC (CRTC) The CRTC sub-device table contains additional sub-device tables where each one corresponds to a CRTC. Each CRTC sub-device must include a property called "id" whose value matches the driver's crtc id. Additional properties for the CRTC are used to configure the crtc. Connector (CNCT) The CNCT sub-device table contains additional sub-device tables where each one corresponds to a connector. Each of the connector sub-device tables must include a property called "name" whose value matches a connector name assigned by the driver (see later patch for output name function). Additional connector properties can be set through these tables. Plane (PLNS) The PLNS sub-device table contains additional sub-device tables where each one corresponds to a plane. [this needs additional work] In addition, the main device property table for the device may contain configuration information that applies to general driver configuration. The framework includes a couple of helper functions to access the configuration data. intel_config_get_integer() will look up a configuration property and return the integer value associated with it. intel_config_init_<component>_property() will look up a configuration property and assign the value to a drm property of the same name. These functions are used to initialize drm property instances to specific values. Signed-off-by: Bob Paauwe <bob.j.paauwe@xxxxxxxxx> --- drivers/gpu/drm/i915/Makefile | 3 +- drivers/gpu/drm/i915/i915_dma.c | 4 + drivers/gpu/drm/i915/i915_drv.h | 16 ++ drivers/gpu/drm/i915/i915_params.c | 6 + drivers/gpu/drm/i915/intel_config.c | 542 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 28 ++ 6 files changed, 598 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i915/intel_config.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index f025e7f..462de19 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -12,7 +12,8 @@ i915-y := i915_drv.o \ i915_suspend.o \ i915_sysfs.o \ intel_pm.o \ - intel_runtime_pm.o + intel_runtime_pm.o \ + intel_config.o i915-$(CONFIG_COMPAT) += i915_ioc32.o i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5804aa5..9501360 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -656,6 +656,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev->dev_private = dev_priv; dev_priv->dev = dev; + intel_config_init(dev); + /* Setup the write-once "constant" device info */ device_info = (struct intel_device_info *)&dev_priv->info; memcpy(device_info, info, sizeof(dev_priv->info)); @@ -929,6 +931,8 @@ int i915_driver_unload(struct drm_device *dev) acpi_video_unregister(); + intel_config_shutdown(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) intel_fbdev_fini(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2dedd43..165091c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1645,6 +1645,20 @@ struct i915_virtual_gpu { bool active; }; +struct intel_config_node { + struct acpi_device *adev; + struct list_head node; + struct list_head list; +}; + +struct intel_config_info { + struct intel_config_node base; + struct list_head crtc_list; + struct list_head connector_list; + struct list_head plane_list; +}; + + struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -1886,6 +1900,7 @@ struct drm_i915_private { u32 long_hpd_port_mask; u32 short_hpd_port_mask; struct work_struct dig_port_work; + struct intel_config_info *config_info; /* * if we get a HPD irq from DP and a HPD irq from non-DP @@ -2528,6 +2543,7 @@ struct i915_params { int enable_ips; int invert_brightness; int enable_cmd_parser; + char cfg_firmware[PATH_MAX]; /* leave bools at the end to not create holes */ bool enable_hangcheck; bool fastboot; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 44f2262..f92621c 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -53,6 +53,7 @@ struct i915_params i915 __read_mostly = { .mmio_debug = 0, .verbose_state_checks = 1, .nuclear_pageflip = 0, + .cfg_firmware = "", }; module_param_named(modeset, i915.modeset, int, 0400); @@ -183,3 +184,8 @@ MODULE_PARM_DESC(verbose_state_checks, module_param_named_unsafe(nuclear_pageflip, i915.nuclear_pageflip, bool, 0600); MODULE_PARM_DESC(nuclear_pageflip, "Force atomic modeset functionality; only planes work for now (default: false)."); + +module_param_string(cfg_firmware, i915.cfg_firmware, sizeof(i915.cfg_firmware), 0444); +MODULE_PARM_DESC(cfg_firmware, + "Load configuration firmware from built-in data or /lib/firmware. "); + diff --git a/drivers/gpu/drm/i915/intel_config.c b/drivers/gpu/drm/i915/intel_config.c new file mode 100644 index 0000000..cf7da93 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_config.c @@ -0,0 +1,542 @@ +/* + * i915 configuration via ACPI device properties. + * + * Copyright (C) 2014, 2015 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/ctype.h> +#include <acpi/acpi_bus.h> +#include "intel_drv.h" +#include <drm/i915_drm.h> +#include "i915_drv.h" +#include "i915_trace.h" +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + + +#define i915_ACPI_PRP_ROOT "\\_SB.PRP.GFX0" + +/* + * Load an ACPI property table into the ACPI subsystem. + * + * First, verify that a table isn't already loaded. The table may + * be part of the ACPI firmware and thus loaded by the ACPI sub-system + * during boot. It is also possible for ACPI sub-system to load tables + * to override those supplied by the firmware. + * + * If a property table for the GFX device has not been loaded, attempt + * to load one from /lib/firmware here. The name will default to + * drm_i915.aml but the name be overridden by the cfg_firmware module + * parameter. + * + * The order of precidence for table loading is: + * - dyanamic table loaded by ACPI driver + * - table built into firmware + * - dynamic table loaded based on driver name or module parameter + * + * If the table is loaded by the driver, it will be unloaded when the + * driver module is unloaded. Tables that are part of the firmware or + * overridden by the ACPI driver are not unloaded and cannot be replaced + * by tables loaded by the driver. + */ +static int intel_acpi_load_table(struct drm_device *dev, char *firmware) +{ + struct acpi_table_header *table; + acpi_status status; + acpi_handle handle; + struct acpi_device *acpi_dev = NULL; + const struct firmware *fw; + int ret = 0; + + /* make sure the table isn't already loaded before loading it */ + status = acpi_get_handle(ACPI_ROOT_OBJECT, i915_ACPI_PRP_ROOT, &handle); + if (ACPI_FAILURE(status)) { + + /* Try to dynamically load a table.... */ + DRM_DEBUG_DRIVER("Requesting configuration table: %s\n", + firmware); + ret = request_firmware(&fw, firmware, dev->dev); + if (ret != 0) { + DRM_ERROR("Failed to find ACPI table %s: %d\n", + firmware, ret); + fw = NULL; + goto bad_table; + } else { + table = (struct acpi_table_header *)fw->data; + } + + /* Load the table into the acpi device. */ + status = acpi_load_table(table); + if (ACPI_FAILURE(status)) + goto bad_table; + + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, true); + + /* Get a handle and scan the bus */ + status = acpi_get_handle(ACPI_ROOT_OBJECT, i915_ACPI_PRP_ROOT, + &handle); + acpi_bus_scan(handle); + + if (acpi_bus_get_device(handle, &acpi_dev)) + goto bad_table; + + acpi_bind_one(dev->dev, acpi_dev); + + } else { + DRM_DEBUG_DRIVER("ACPI Property table previously loaded for _SB.PRP.GFX0\n"); + } + + return 0; + +bad_table: + release_firmware(fw); + return -ENODEV; +} + +/* + * Use ACPI methods to get the property. + * + * This isn't using the generic device_property_read because that + * can only access properties associated with the actual device. It + * doesn't understand our sub-component property tree. + */ +static bool node_property(struct intel_config_node *n, + const char *prop, + void *value) +{ + int ret = 0; + const union acpi_object *obj; + + ret = acpi_dev_get_property(n->adev, prop, ACPI_TYPE_ANY, &obj); + if (ret == -ENODATA) { + /* + * This isn't really a failure, it's ok if the property + * doesn't exist. The message is for debug purposes only. + */ + DRM_DEBUG_DRIVER("Property \"%s\" not found in %s\n", + prop, acpi_device_bid(n->adev)); + } else if (ret == -EINVAL) { + DRM_ERROR("Invalid acpi device or property name.\n"); + } else if (ret) { + /* This is a failure. */ + DRM_ERROR("Property request failed for %s: %d\n", prop, ret); + } else { + switch (obj->type) { + case ACPI_TYPE_INTEGER: + *(u32 *)value = obj->integer.value; + break; + case ACPI_TYPE_STRING: + *(char **)value = obj->string.pointer; + break; + default: + DRM_ERROR("Unsupported property type, only support integer and string.\n"); + ret = -1; + break; + } + } + + return ret == 0; +} + + +/** + * intel_config_init - + * + * Initialize the configuration framework by attempting to load a + * property table and parse the contents into component lists. + * + * @dev: The drm driver device. + */ +void intel_config_init(struct drm_device *dev) +{ + struct intel_config_info *info; + struct intel_config_node *new_node, *tmp; + acpi_handle handle; + struct acpi_device *cl, *component; + char *cname; + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Load an ACPI table from /lib/firmware. */ + snprintf(i915.cfg_firmware, PATH_MAX, "drm_%s.aml", dev->driver->name); + + if (intel_acpi_load_table(dev, i915.cfg_firmware) != 0) { + DRM_ERROR("Failed to load ACPI device property table.\n"); + return; + } + + DRM_DEBUG_DRIVER("Loaded ACPI configuration for %s\n", + dev->driver->name); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + DRM_ERROR("Failed to allocate memory for configuration.\n"); + return; + } + + INIT_LIST_HEAD(&info->base.list); + INIT_LIST_HEAD(&info->crtc_list); + INIT_LIST_HEAD(&info->connector_list); + INIT_LIST_HEAD(&info->plane_list); + + handle = ACPI_HANDLE(dev->dev); + if (!handle) { + DRM_DEBUG_DRIVER("No associated ACPI handle.\n"); + kfree(info); + return; + } else { + acpi_bus_get_device(handle, &info->base.adev); + } + + /* + * Create a list of one for the top level driver config. + * + * We don't really need a full config_info structure for this but + * it simplifies the handling of driver general config settings + * as we don't have to have a special case and unique structure + * just for this. + */ + INIT_LIST_HEAD(&info->base.node); + list_add_tail(&info->base.node, &info->base.list); + +/* Component sub-device ACPI names */ +#define i915_COMPONENT_CRTC "CRTC" +#define i915_COMPONENT_CONNECTOR "CNCT" +#define i915_COMPONENT_PLANE "PLNS" + + /* Build lists */ + list_for_each_entry(component, &info->base.adev->children, node) { + if (!component) + continue; + + cname = acpi_device_bid(component); + + list_for_each_entry(cl, &component->children, node) { + new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) + goto bail; + new_node->adev = cl; + INIT_LIST_HEAD(&new_node->node); + + /* Add to the appropriate list */ + if (strcmp(cname, i915_COMPONENT_CRTC) == 0) { + list_add_tail(&new_node->node, + &info->crtc_list); + } else if (strcmp(cname, i915_COMPONENT_CONNECTOR) == 0) { + list_add_tail(&new_node->node, + &info->connector_list); + } else if (strcmp(cname, i915_COMPONENT_PLANE) == 0) { + list_add_tail(&new_node->node, + &info->plane_list); + } else { + /* unknown component, ignore it */ + kfree(new_node); + } + } + } + + dev_priv->config_info = info; + DRM_DEBUG_DRIVER("i915 Configuration loaded.\n"); + return; + +bail: + DRM_DEBUG_DRIVER("i915 Configuration aborted, insufficient memory.\n"); + list_for_each_entry_safe(new_node, tmp, &info->crtc_list, node) + kfree(new_node); + list_for_each_entry_safe(new_node, tmp, &info->connector_list, node) + kfree(new_node); + list_for_each_entry_safe(new_node, tmp, &info->plane_list, node) + kfree(new_node); + + kfree(info); + dev_priv->config_info = NULL; +} + + +/** + * Clean up the configuration infrastructure as the driver is + * shuting down. Don't leak memory! + * + * @dev: The drm driver device. + */ +void intel_config_shutdown(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + acpi_handle handle; + acpi_status status; + struct acpi_device *adev; + struct intel_config_info *info; + struct intel_config_node *n, *tmp; + + if (dev_priv->config_info == NULL) + return; + + /* Free the component lists. */ + info = dev_priv->config_info; + list_for_each_entry_safe(n, tmp, &info->crtc_list, node) + kfree(n); + list_for_each_entry_safe(n, tmp, &info->connector_list, node) + kfree(n); + list_for_each_entry_safe(n, tmp, &info->plane_list, node) + kfree(n); + + /* Unload any dynamically loaded ACPI property table */ + handle = ACPI_HANDLE(dev->dev); + + DRM_DEBUG_DRIVER("Unloading dynamic i915 Configuration ACPI table.\n"); + if (handle) { + + acpi_unbind_one(dev->dev); + + if (acpi_bus_get_device(handle, &adev)) + DRM_ERROR("Failed to get ACPI bus device.\n"); + else + acpi_bus_trim(adev); + + status = acpi_unload_parent_table(handle); + if (ACPI_FAILURE(status)) + DRM_ERROR("Failed to unload the i915 Configuration" + "ACPI table. %d\n", status); + } +} + + + +/* + * does_name_match + * + * The various drm components have names assocated with them. To link + * a component in the ACPI property tree, use a "special" property + * called "name". + * + * The exception is the general driver properties. There is no "name" + * property associated with those. We could force one, but that may + * be less intuitive than leaving the name empty. + * + * This function look for a property called "name" and compares the + * value of that property with the passed in name parameter. + */ +static bool does_name_match(struct intel_config_node *node, const char *name) +{ + char *p_name; + + /* + * General driver properties aren't in a section with a "name" + * property. Thus this should just return true in that case. + */ + if (!name || strlen(name) == 0) + return true; + + + /* Look up a name property and see if it matches */ + if (node_property(node, "name", &p_name)) { + if (strcmp(name, p_name) == 0) + return true; + } + + return false; +} + + +/* + * Map the configuration sub component enum to the cached + * sub component list. + */ +static struct list_head *cfg_type_to_list(struct intel_config_info *info, + enum cfg_type cfg_type) +{ + switch (cfg_type) { + case CFG_DRV: + return &info->base.list; + case CFG_CRTC: + return &info->crtc_list; + case CFG_CONNECTOR: + return &info->connector_list; + case CFG_PLANE: + return &info->plane_list; + } + return NULL; +} + +/* + * Integer property. + * + * Look up a property and set the value pointer to the property value. + * + * returns true if the property was found, false if not. + */ +bool intel_config_get_integer(struct drm_i915_private *dev_priv, + enum cfg_type cfg_type, + const char *name, + const char *property, + uint32_t *value) +{ + struct intel_config_info *info = dev_priv->config_info; + struct intel_config_node *n; + struct list_head *list; + bool ret = false; + + if (!info) + return false; + + list = cfg_type_to_list(info, cfg_type); + if (!list) + return false; + + list_for_each_entry(n, list, node) { + if (does_name_match(n, name)) { + if (node_property(n, property, value)) + return true; + } + } + + return false; +} + +/* + * Look up a drm property name in the configuration store and if + * found, return the value. + * + * A default value is included in the parameter list so that something + * reasonable is returned if the lookup fails to find a matching + * configuration key. + */ +static uint64_t lookup_property(struct drm_i915_private *dev_priv, + const char *name, + struct drm_property *property, + enum cfg_type cfg_type, + uint64_t dflt) +{ + struct intel_config_info *info = dev_priv->config_info; + struct drm_property_enum *prop_enum; + const char *value; + uint64_t retval = dflt; + struct intel_config_node *n; + struct list_head *list; + + list = cfg_type_to_list(info, cfg_type); + if (!list) + goto out; + + list_for_each_entry(n, list, node) { + if (!does_name_match(n, name)) + continue; + /* + * FIXME: This is assuming that the type is string + * for enun drm properties. This should be more + * generic and map drm property types to ACPI property + * types. + */ + if (!node_property(n, property->name, &value)) + continue; + + /* Look for a matching enum */ + list_for_each_entry(prop_enum, &property->enum_list, head) { + if (strcmp(value, prop_enum->name) == 0) { + retval = prop_enum->value; + goto out; + } + } + } + +out: + return retval; +} + +/* + * Connector properties. + * + * If a connector drm property has a corresponding configuration property, + * use the configuration property value to initialize the drm property. + */ +uint64_t intel_config_init_connector_property(struct drm_connector *connector, + const char *name, + struct drm_property *property, + uint64_t dflt) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct intel_config_info *info = dev_priv->config_info; + uint64_t retval; + + if (!info) + goto out; + + retval = lookup_property(dev_priv, name, property, CFG_CONNECTOR, dflt); + +out: + drm_object_attach_property(&connector->base, property, retval); + return retval; +} + + +/* + * Plane properties. + * + * If a plane drm property has a corresponding configuration property, + * use the configuration property value to initialize the drm property. + */ +uint64_t intel_config_init_plane_property(struct drm_plane *plane, + const char *name, + struct drm_property *property, + uint64_t dflt) +{ + struct drm_i915_private *dev_priv = plane->dev->dev_private; + struct intel_config_info *info = dev_priv->config_info; + uint64_t retval; + + if (!info) + goto out; + + retval = lookup_property(dev_priv, name, property, CFG_PLANE, dflt); + +out: + drm_object_attach_property(&plane->base, property, retval); + return retval; +} + + +/* + * CRTC properties. + * + * If a crtc drm property has a corresponding configuration property, + * use the configuration property value to initialize the drm property. + */ +uint64_t intel_config_init_crtc_property(struct drm_crtc *crtc, + const char *name, + struct drm_property *property, + uint64_t dflt) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_config_info *info = dev_priv->config_info; + uint64_t retval; + + if (!info) + goto out; + + retval = lookup_property(dev_priv, name, property, CFG_CRTC, dflt); + +out: + drm_object_attach_property(&crtc->base, property, retval); + return retval; +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1de8e20..aefd95e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1259,4 +1259,32 @@ void intel_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); extern const struct drm_plane_helper_funcs intel_plane_helper_funcs; +/* intel_config.c */ +enum cfg_type { + CFG_DRV, + CFG_CRTC, + CFG_CONNECTOR, + CFG_PLANE +}; + +void intel_config_init(struct drm_device *dev); +void intel_config_shutdown(struct drm_device *dev); +bool intel_config_get_integer(struct drm_i915_private *dev_priv, + enum cfg_type cfg_type, + const char *name, + const char *property, + uint32_t *value); +uint64_t intel_config_init_connector_property(struct drm_connector *connector, + const char *name, + struct drm_property *property, + uint64_t dflt); +uint64_t intel_config_init_plane_property(struct drm_plane *plane, + const char *name, + struct drm_property *property, + uint64_t dflt); +uint64_t intel_config_init_crtc_property(struct drm_crtc *crtc, + const char *name, + struct drm_property *property, + uint64_t dflt); + #endif /* __INTEL_DRV_H__ */ -- 2.1.0 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx