From: Dave Gordon <david.s.gordon@xxxxxxxxx> Factor out the common code of loading firmware into a new file, leaving only the uC-specific parts in the GuC loaders. Issue: VIZ-4884 Signed-off-by: Alex Dai <yu.dai@xxxxxxxxx> Signed-off-by: Dave Gordon <david.s.gordon@xxxxxxxxx> --- drivers/gpu/drm/i915/Makefile | 3 + drivers/gpu/drm/i915/intel_uc_loader.c | 208 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_uc_loader.h | 78 +++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 drivers/gpu/drm/i915/intel_uc_loader.c create mode 100644 drivers/gpu/drm/i915/intel_uc_loader.h diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index a69002e..1b027c7 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -39,6 +39,9 @@ i915-y += i915_cmd_parser.o \ intel_ringbuffer.o \ intel_uncore.o +# ancilliary microcontroller support +i915-y += intel_uc_loader.o + # autogenerated null render state i915-y += intel_renderstate_gen6.o \ intel_renderstate_gen7.o \ diff --git a/drivers/gpu/drm/i915/intel_uc_loader.c b/drivers/gpu/drm/i915/intel_uc_loader.c new file mode 100644 index 0000000..249e56e --- /dev/null +++ b/drivers/gpu/drm/i915/intel_uc_loader.c @@ -0,0 +1,208 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ +#include <linux/firmware.h> +#include "i915_drv.h" +#include "intel_uc_loader.h" + +/* + * Called once per uC, late in driver initialisation. GEM is now ready, and so + * we can now create a GEM object to hold the uC firmware. But first, we must + * synchronise with the firmware-fetching thread that was initiated during + * early driver load, in intel_uc_fw_init(), and see whether it successfully + * fetched the firmware blob. + */ +static void uc_fw_finish(struct drm_device *dev, struct intel_uc_fw *uc_fw) +{ + struct drm_i915_gem_object *obj = NULL; + const struct firmware *fw; + + DRM_DEBUG_DRIVER("before waiting: %s fw fetch status %d, fw %p\n", + uc_fw->uc_name, uc_fw->uc_fw_fetch_status, uc_fw->uc_fw_blob); + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + WARN_ON(uc_fw->uc_fw_fetch_status != INTEL_UC_FIRMWARE_PENDING); + + wait_for_completion(&uc_fw->uc_fw_fetched); + + DRM_DEBUG_DRIVER("after waiting: %s fw fetch status %d, fw %p\n", + uc_fw->uc_name, uc_fw->uc_fw_fetch_status, uc_fw->uc_fw_blob); + + fw = uc_fw->uc_fw_blob; + uc_fw->uc_fw_blob = NULL; + if (!fw) + goto fail; + + obj = i915_gem_alloc_object(dev, round_up(fw->size, PAGE_SIZE)); + if (!obj) + goto fail; + + if (i915_gem_object_write(obj, fw->data, fw->size)) + goto fail; + + DRM_DEBUG_DRIVER("%s fw fetch status SUCCESS\n", uc_fw->uc_name); + uc_fw->uc_fw_obj = obj; + uc_fw->uc_fw_size = fw->size; + uc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_SUCCESS; + release_firmware(fw); + return; + +fail: + DRM_DEBUG_DRIVER("%s fw fetch status FAIL; fw %p, obj %p\n", + uc_fw->uc_name, fw, obj); + DRM_ERROR("Failed to fetch %s firmware from <%s>\n", + uc_fw->uc_name, uc_fw->uc_fw_path); + + if (obj) + drm_gem_object_unreference(&obj->base); + + uc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_FAIL; + release_firmware(fw); /* OK even if fw is NULL */ +} + +/* + * Check the status of the firmware fetching process. If it's still + * PENDING, wait for completion first, then check the outcome. + */ +int intel_uc_fw_check(struct drm_device *dev, struct intel_uc_fw *uc_fw) +{ + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + if (uc_fw->uc_fw_fetch_status == INTEL_UC_FIRMWARE_PENDING) { + /* We only come here once */ + uc_fw_finish(dev, uc_fw); + /* state must now be FAIL or SUCCESS */ + } + + DRM_DEBUG_DRIVER("%s fw fetch status %d\n", + uc_fw->uc_name, uc_fw->uc_fw_fetch_status); + + switch (uc_fw->uc_fw_fetch_status) { + case INTEL_UC_FIRMWARE_FAIL: + /* something went wrong :( */ + return -EIO; + + case INTEL_UC_FIRMWARE_NONE: + /* no firmware, nothing to do (not an error) */ + return 0; + + case INTEL_UC_FIRMWARE_PENDING: + default: + /* "can't happen" */ + WARN_ONCE(1, "%s fw <%s> invalid uc_fw_fetch_status %d!\n", + uc_fw->uc_name, uc_fw->uc_fw_path, + uc_fw->uc_fw_fetch_status); + return -ENXIO; + + case INTEL_UC_FIRMWARE_SUCCESS: + return 0; + } +} + +/* + * Callback from the kernel's asynchronous firmware-fetching subsystem. + * All we have to do is stash the blob and signal completion. + * Error checking (e.g. no firmware) is left to mainline code. + * We don't have (and don't want or need to acquire) the struct_mutex here. + */ +static void uc_fw_fetch_callback(const struct firmware *fw, void *context) +{ + struct intel_uc_fw *uc_fw = context; + + WARN_ON(uc_fw->uc_fw_fetch_status != INTEL_UC_FIRMWARE_PENDING); + DRM_DEBUG_DRIVER("%s firmware fetch from <%s> status %d, fw %p\n", + uc_fw->uc_name, uc_fw->uc_fw_path, + uc_fw->uc_fw_fetch_status, fw); + + uc_fw->uc_fw_blob = fw; + complete(&uc_fw->uc_fw_fetched); +} + +/* + * This is called just once per uC, during driver loading. It is therefore + * automatically single-threaded and does not need to acquire any mutexes + * or spinlocks. OTOH, GEM is not yet fully initialised, so we can't do + * very much here. + * + * The main task here is to initiate the fetching of the uC firmware into + * memory, using the standard kernel firmware fetching support. The actual + * fetching will then proceed asynchronously and in parallel with the rest + * of driver initialisation; later in the loading process we will synchronise + * with the firmware-fetching thread before transferring the firmware image + * firstly into a GEM object and then into the uC's memory. + */ +void intel_uc_fw_init(struct drm_device *dev, struct intel_uc_fw *uc_fw, + const char *name, const char *fw_path, bool has_ucode) +{ + uc_fw->uc_name = name; + uc_fw->uc_fw_path = fw_path; + uc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_NONE; + init_completion(&uc_fw->uc_fw_fetched); + + if (!has_ucode) + return; + if (fw_path == NULL) { + DRM_ERROR("No known %s firmware on this platform\n", name); + uc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_FAIL; + return; + } + + uc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_PENDING; + + if (request_firmware_nowait(THIS_MODULE, true, fw_path, + &dev->pdev->dev, + GFP_KERNEL, uc_fw, + uc_fw_fetch_callback)) { + DRM_ERROR("Failed to request %s firmware from <%s>\n", + name, fw_path); + uc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_FAIL; + return; + } + + /* firmware fetch initiated, callback will signal completion */ + DRM_DEBUG_DRIVER("initiated fetching %s firmware from <%s>\n", + name, fw_path); +} + +/* + * Clean up all uC firmware-related data + */ +void intel_uc_fw_fini(struct drm_device *dev, struct intel_uc_fw *uc_fw) +{ + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + /* + * Generally, the blob should have been released earlier, but + * if the driver load were aborted after the fetch had been + * initiated but not completed it might still be around + */ + if (uc_fw->uc_fw_fetch_status == INTEL_UC_FIRMWARE_PENDING) + wait_for_completion(&uc_fw->uc_fw_fetched); + if (uc_fw->uc_fw_blob) + release_firmware(uc_fw->uc_fw_blob); + uc_fw->uc_fw_blob = NULL; + + if (uc_fw->uc_fw_obj) + drm_gem_object_unreference(&uc_fw->uc_fw_obj->base); + uc_fw->uc_fw_obj = NULL; +} diff --git a/drivers/gpu/drm/i915/intel_uc_loader.h b/drivers/gpu/drm/i915/intel_uc_loader.h new file mode 100644 index 0000000..661d46a --- /dev/null +++ b/drivers/gpu/drm/i915/intel_uc_loader.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ +#ifndef _INTEL_UC_LOADER_H +#define _INTEL_UC_LOADER_H + +/* + * Microcontroller (uC) firmware loading support + */ + +/* + * These values are used to track the stages of getting the required firmware + * into an onboard microcontroller. The common code tracks the phases of + * fetching the firmware (aka "binary blob") from an external file into a GEM + * object in the 'uc_fw_fetch_status' field below; the uC-specific DMA code + * uses the 'uc_fw_load_status' field to track the transfer from GEM object + * to uC memory. + * + * For the first (fetch) stage, the interpretation of the values is: + * NONE - no firmware is being fetched e.g. because there is no uC + * PENDING - firmware fetch initiated; callback will complete 'uc_fw_fetched' + * SUCCESS - uC firmware fetched into a GEM object and ready for use + * FAIL - something went wrong; uC firmware is not available + * + * The second (load) stage is simpler as there is no asynchronous handoff: + * NONE - no firmware is being loaded e.g. because there is no uC + * PENDING - firmware DMA load in progress + * SUCCESS - uC firmware loaded into uC memory and ready for use + * FAIL - something went wrong; uC firmware is not available + */ +enum intel_uc_fw_status { + INTEL_UC_FIRMWARE_FAIL = -1, + INTEL_UC_FIRMWARE_NONE = 0, + INTEL_UC_FIRMWARE_PENDING, + INTEL_UC_FIRMWARE_SUCCESS +}; + +/* + * This structure encapsulates all the data needed during the process of + * fetching, caching, and loading the firmware image into the uC. + */ +struct intel_uc_fw { + const char * uc_name; + const char * uc_fw_path; + const struct firmware * uc_fw_blob; + struct completion uc_fw_fetched; + size_t uc_fw_size; + struct drm_i915_gem_object * uc_fw_obj; + enum intel_uc_fw_status uc_fw_fetch_status; + enum intel_uc_fw_status uc_fw_load_status; +}; + +void intel_uc_fw_init(struct drm_device *dev, struct intel_uc_fw *uc_fw, + const char *uc_name, const char *fw_path, bool has_ucode); +int intel_uc_fw_check(struct drm_device *dev, struct intel_uc_fw *uc_fw); +void intel_uc_fw_fini(struct drm_device *dev, struct intel_uc_fw *uc_fw); + +#endif -- 1.9.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx