Commit bd132858 authored by Anusha Srivatsa's avatar Anusha Srivatsa Committed by Jani Nikula

drm/i915/huc: Add HuC fw loading support

The HuC loading process is similar to GuC. The intel_uc_fw_fetch()
is used for both cases.

HuC loading needs to be before GuC loading. The WOPCM setting must
be done early before loading any of them.

v2: rebased on-top of drm-intel-nightly.
    removed if(HAS_GUC()) before the guc call. (D.Gordon)
    update huc_version number of format.
v3: rebased to drm-intel-nightly, changed the file name format to
    match the one in the huc package.
    Changed dev->dev_private to to_i915()
v4: moved function back to where it was.
    change wait_for_atomic to wait_for.
v5: rebased. Changed the year in the copyright message to reflect
the right year.Correct the comments,remove the unwanted WARN message,
replace drm_gem_object_unreference() with i915_gem_object_put().Make the
prototypes in intel_huc.h non-extern.
v6: rebased. Update the file construction done by HuC. It is similar to
GuC.Adopted the approach used in-
https://patchwork.freedesktop.org/patch/104355/ <Tvrtko Ursulin>
v7: Change dev to dev_priv in macro definition.
Corrected comments.
v8: rebased on top of drm-tip. Updated functions intel_huc_load(),
intel_huc_init() and intel_uc_fw_fetch() to accept dev_priv instead of
dev. Moved contents of intel_huc.h to intel_uc.h.
v9: change SKL_FW_ to SKL_HUC_FW_. Add intel_ prefix to guc_wopcm_size().
Remove unwanted checks in intel_uc.h. Rename huc_fw in struct intel_huc to
simply fw to avoid redundency.
v10: rebased. Correct comments. Make intel_huc_fini() accept dev_priv
instead of dev like intel_huc_init() and intel_huc_load().Move definition
to i915_guc_reg.h from intel_uc.h. Clean DMA_CTRL bits after HuC DMA
transfer in huc_ucode_xfer() instead of guc_ucode_xfer(). Add suitable
WARNs to give extra info.
v11: rebased. Add proper bias for HuC and make sure there are
asserts on failure by using guc_ggtt_offset_vma(). Introduce
intel_huc.c and remove intel_huc_loader.c since it has functions that
do more than just loading.Correct year in copyright.
v12: remove invalidates that are not required anymore.

Cc: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
Tested-by: default avatarXiang Haihao <haihao.xiang@intel.com>
Signed-off-by: default avatarAnusha Srivatsa <anusha.srivatsa@intel.com>
Signed-off-by: default avatarAlex Dai <yu.dai@intel.com>
Signed-off-by: default avatarPeter Antoine <peter.antoine@intel.com>
Reviewed-by: default avatarMichal Wajdeczko <michal.wajdeczko@intel.com>
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1484755558-1234-1-git-send-email-anusha.srivatsa@intel.com
parent 5b8cd075
...@@ -58,6 +58,7 @@ i915-y += i915_cmd_parser.o \ ...@@ -58,6 +58,7 @@ i915-y += i915_cmd_parser.o \
i915-y += intel_uc.o \ i915-y += intel_uc.o \
intel_guc_log.o \ intel_guc_log.o \
intel_guc_loader.o \ intel_guc_loader.o \
intel_huc.o \
i915_guc_submission.o i915_guc_submission.o
# autogenerated null render state # autogenerated null render state
......
...@@ -599,6 +599,7 @@ static int i915_load_modeset_init(struct drm_device *dev) ...@@ -599,6 +599,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret) if (ret)
goto cleanup_irq; goto cleanup_irq;
intel_huc_init(dev_priv);
intel_guc_init(dev_priv); intel_guc_init(dev_priv);
ret = i915_gem_init(dev_priv); ret = i915_gem_init(dev_priv);
...@@ -627,6 +628,7 @@ static int i915_load_modeset_init(struct drm_device *dev) ...@@ -627,6 +628,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
i915_gem_fini(dev_priv); i915_gem_fini(dev_priv);
cleanup_irq: cleanup_irq:
intel_guc_fini(dev_priv); intel_guc_fini(dev_priv);
intel_huc_fini(dev_priv);
drm_irq_uninstall(dev); drm_irq_uninstall(dev);
intel_teardown_gmbus(dev_priv); intel_teardown_gmbus(dev_priv);
cleanup_csr: cleanup_csr:
...@@ -1314,6 +1316,7 @@ void i915_driver_unload(struct drm_device *dev) ...@@ -1314,6 +1316,7 @@ void i915_driver_unload(struct drm_device *dev)
drain_workqueue(dev_priv->wq); drain_workqueue(dev_priv->wq);
intel_guc_fini(dev_priv); intel_guc_fini(dev_priv);
intel_huc_fini(dev_priv);
i915_gem_fini(dev_priv); i915_gem_fini(dev_priv);
intel_fbc_cleanup_cfb(dev_priv); intel_fbc_cleanup_cfb(dev_priv);
......
...@@ -2073,6 +2073,7 @@ struct drm_i915_private { ...@@ -2073,6 +2073,7 @@ struct drm_i915_private {
struct intel_gvt *gvt; struct intel_gvt *gvt;
struct intel_huc huc;
struct intel_guc guc; struct intel_guc guc;
struct intel_csr csr; struct intel_csr csr;
...@@ -2847,6 +2848,7 @@ intel_info(const struct drm_i915_private *dev_priv) ...@@ -2847,6 +2848,7 @@ intel_info(const struct drm_i915_private *dev_priv)
#define HAS_GUC(dev_priv) ((dev_priv)->info.has_guc) #define HAS_GUC(dev_priv) ((dev_priv)->info.has_guc)
#define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) #define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv))
#define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv)) #define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv))
#define HAS_HUC_UCODE(dev_priv) (HAS_GUC(dev_priv))
#define HAS_RESOURCE_STREAMER(dev_priv) ((dev_priv)->info.has_resource_streamer) #define HAS_RESOURCE_STREAMER(dev_priv) ((dev_priv)->info.has_resource_streamer)
......
...@@ -61,12 +61,18 @@ ...@@ -61,12 +61,18 @@
#define DMA_ADDRESS_SPACE_GTT (8 << 16) #define DMA_ADDRESS_SPACE_GTT (8 << 16)
#define DMA_COPY_SIZE _MMIO(0xc310) #define DMA_COPY_SIZE _MMIO(0xc310)
#define DMA_CTRL _MMIO(0xc314) #define DMA_CTRL _MMIO(0xc314)
#define HUC_UKERNEL (1<<9)
#define UOS_MOVE (1<<4) #define UOS_MOVE (1<<4)
#define START_DMA (1<<0) #define START_DMA (1<<0)
#define DMA_GUC_WOPCM_OFFSET _MMIO(0xc340) #define DMA_GUC_WOPCM_OFFSET _MMIO(0xc340)
#define HUC_LOADING_AGENT_VCR (0<<1)
#define HUC_LOADING_AGENT_GUC (1<<1)
#define GUC_WOPCM_OFFSET_VALUE 0x80000 /* 512KB */ #define GUC_WOPCM_OFFSET_VALUE 0x80000 /* 512KB */
#define GUC_MAX_IDLE_COUNT _MMIO(0xC3E4) #define GUC_MAX_IDLE_COUNT _MMIO(0xC3E4)
#define HUC_STATUS2 _MMIO(0xD3B0)
#define HUC_FW_VERIFIED (1<<7)
/* Defines WOPCM space available to GuC firmware */ /* Defines WOPCM space available to GuC firmware */
#define GUC_WOPCM_SIZE _MMIO(0xc050) #define GUC_WOPCM_SIZE _MMIO(0xc050)
/* GuC addresses below GUC_WOPCM_TOP don't map through the GTT */ /* GuC addresses below GUC_WOPCM_TOP don't map through the GTT */
......
...@@ -328,7 +328,7 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv, ...@@ -328,7 +328,7 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv,
return ret; return ret;
} }
static u32 guc_wopcm_size(struct drm_i915_private *dev_priv) u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv)
{ {
u32 wopcm_size = GUC_WOPCM_TOP; u32 wopcm_size = GUC_WOPCM_TOP;
...@@ -364,7 +364,7 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) ...@@ -364,7 +364,7 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
/* init WOPCM */ /* init WOPCM */
I915_WRITE(GUC_WOPCM_SIZE, guc_wopcm_size(dev_priv)); I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE); I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE);
/* Enable MIA caching. GuC clock gating is disabled. */ /* Enable MIA caching. GuC clock gating is disabled. */
...@@ -506,6 +506,7 @@ int intel_guc_setup(struct drm_i915_private *dev_priv) ...@@ -506,6 +506,7 @@ int intel_guc_setup(struct drm_i915_private *dev_priv)
if (err) if (err)
goto fail; goto fail;
intel_huc_load(dev_priv);
err = guc_ucode_xfer(dev_priv); err = guc_ucode_xfer(dev_priv);
if (!err) if (!err)
break; break;
...@@ -654,7 +655,7 @@ void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, ...@@ -654,7 +655,7 @@ void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
size = uc_fw->header_size + uc_fw->ucode_size; size = uc_fw->header_size + uc_fw->ucode_size;
/* Top 32k of WOPCM is reserved (8K stack + 24k RC6 context). */ /* Top 32k of WOPCM is reserved (8K stack + 24k RC6 context). */
if (size > guc_wopcm_size(dev_priv)) { if (size > intel_guc_wopcm_size(dev_priv)) {
DRM_ERROR("Firmware is too large to fit in WOPCM\n"); DRM_ERROR("Firmware is too large to fit in WOPCM\n");
goto fail; goto fail;
} }
......
/*
* Copyright © 2016-2017 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.h"
/**
* DOC: HuC Firmware
*
* Motivation:
* GEN9 introduces a new dedicated firmware for usage in media HEVC (High
* Efficiency Video Coding) operations. Userspace can use the firmware
* capabilities by adding HuC specific commands to batch buffers.
*
* Implementation:
* The same firmware loader is used as the GuC. However, the actual
* loading to HW is deferred until GEM initialization is done.
*
* Note that HuC firmware loading must be done before GuC loading.
*/
#define SKL_HUC_FW_MAJOR 01
#define SKL_HUC_FW_MINOR 07
#define SKL_BLD_NUM 1398
#define HUC_FW_PATH(platform, major, minor, bld_num) \
"i915/" __stringify(platform) "_huc_ver" __stringify(major) "_" \
__stringify(minor) "_" __stringify(bld_num) ".bin"
#define I915_SKL_HUC_UCODE HUC_FW_PATH(skl, SKL_HUC_FW_MAJOR, \
SKL_HUC_FW_MINOR, SKL_BLD_NUM)
MODULE_FIRMWARE(I915_SKL_HUC_UCODE);
/**
* huc_ucode_xfer() - DMA's the firmware
* @dev_priv: the drm_i915_private device
*
* Transfer the firmware image to RAM for execution by the microcontroller.
*
* Return: 0 on success, non-zero on failure
*/
static int huc_ucode_xfer(struct drm_i915_private *dev_priv)
{
struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
struct i915_vma *vma;
unsigned long offset = 0;
u32 size;
int ret;
ret = i915_gem_object_set_to_gtt_domain(huc_fw->obj, false);
if (ret) {
DRM_DEBUG_DRIVER("set-domain failed %d\n", ret);
return ret;
}
vma = i915_gem_object_ggtt_pin(huc_fw->obj, NULL, 0, 0,
PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
if (IS_ERR(vma)) {
DRM_DEBUG_DRIVER("pin failed %d\n", (int)PTR_ERR(vma));
return PTR_ERR(vma);
}
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
/* init WOPCM */
I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE |
HUC_LOADING_AGENT_GUC);
/* Set the source address for the uCode */
offset = guc_ggtt_offset(vma) + huc_fw->header_offset;
I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
/* Hardware doesn't look at destination address for HuC. Set it to 0,
* but still program the correct address space.
*/
I915_WRITE(DMA_ADDR_1_LOW, 0);
I915_WRITE(DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM);
size = huc_fw->header_size + huc_fw->ucode_size;
I915_WRITE(DMA_COPY_SIZE, size);
/* Start the DMA */
I915_WRITE(DMA_CTRL, _MASKED_BIT_ENABLE(HUC_UKERNEL | START_DMA));
/* Wait for DMA to finish */
ret = wait_for((I915_READ(DMA_CTRL) & START_DMA) == 0, 100);
DRM_DEBUG_DRIVER("HuC DMA transfer wait over with ret %d\n", ret);
/* Disable the bits once DMA is over */
I915_WRITE(DMA_CTRL, _MASKED_BIT_DISABLE(HUC_UKERNEL));
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
/*
* We keep the object pages for reuse during resume. But we can unpin it
* now that DMA has completed, so it doesn't continue to take up space.
*/
i915_vma_unpin(vma);
return ret;
}
/**
* intel_huc_init() - initiate HuC firmware loading request
* @dev_priv: the drm_i915_private device
*
* Called early during driver load, but after GEM is initialised. The loading
* will continue only when driver explicitly specify firmware name and version.
* All other cases are considered as INTEL_UC_FIRMWARE_NONE either because HW
* is not capable or driver yet support it. And there will be no error message
* for INTEL_UC_FIRMWARE_NONE cases.
*
* The DMA-copying to HW is done later when intel_huc_load() is called.
*/
void intel_huc_init(struct drm_i915_private *dev_priv)
{
struct intel_huc *huc = &dev_priv->huc;
struct intel_uc_fw *huc_fw = &huc->fw;
const char *fw_path = NULL;
huc_fw->path = NULL;
huc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE;
huc_fw->load_status = INTEL_UC_FIRMWARE_NONE;
huc_fw->fw = INTEL_UC_FW_TYPE_HUC;
if (!HAS_HUC_UCODE(dev_priv))
return;
if (IS_SKYLAKE(dev_priv)) {
fw_path = I915_SKL_HUC_UCODE;
huc_fw->major_ver_wanted = SKL_HUC_FW_MAJOR;
huc_fw->minor_ver_wanted = SKL_HUC_FW_MINOR;
}
huc_fw->path = fw_path;
huc_fw->fetch_status = INTEL_UC_FIRMWARE_PENDING;
DRM_DEBUG_DRIVER("HuC firmware pending, path %s\n", fw_path);
WARN(huc_fw->path == NULL, "HuC present but no fw path\n");
intel_uc_fw_fetch(dev_priv, huc_fw);
}
/**
* intel_huc_load() - load HuC uCode to device
* @dev_priv: the drm_i915_private device
*
* Called from guc_setup() during driver loading and also after a GPU reset.
* Be note that HuC loading must be done before GuC loading.
*
* The firmware image should have already been fetched into memory by the
* earlier call to intel_huc_init(), so here we need only check that
* is succeeded, and then transfer the image to the h/w.
*
* Return: non-zero code on error
*/
int intel_huc_load(struct drm_i915_private *dev_priv)
{
struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
int err;
if (huc_fw->fetch_status == INTEL_UC_FIRMWARE_NONE)
return 0;
DRM_DEBUG_DRIVER("%s fw status: fetch %s, load %s\n",
huc_fw->path,
intel_uc_fw_status_repr(huc_fw->fetch_status),
intel_uc_fw_status_repr(huc_fw->load_status));
if (huc_fw->fetch_status == INTEL_UC_FIRMWARE_SUCCESS &&
huc_fw->load_status == INTEL_UC_FIRMWARE_FAIL)
return -ENOEXEC;
huc_fw->load_status = INTEL_UC_FIRMWARE_PENDING;
switch (huc_fw->fetch_status) {
case INTEL_UC_FIRMWARE_FAIL:
/* something went wrong :( */
err = -EIO;
goto fail;
case INTEL_UC_FIRMWARE_NONE:
case INTEL_UC_FIRMWARE_PENDING:
default:
/* "can't happen" */
WARN_ONCE(1, "HuC fw %s invalid fetch_status %s [%d]\n",
huc_fw->path,
intel_uc_fw_status_repr(huc_fw->fetch_status),
huc_fw->fetch_status);
err = -ENXIO;
goto fail;
case INTEL_UC_FIRMWARE_SUCCESS:
break;
}
err = huc_ucode_xfer(dev_priv);
if (err)
goto fail;
huc_fw->load_status = INTEL_UC_FIRMWARE_SUCCESS;
DRM_DEBUG_DRIVER("%s fw status: fetch %s, load %s\n",
huc_fw->path,
intel_uc_fw_status_repr(huc_fw->fetch_status),
intel_uc_fw_status_repr(huc_fw->load_status));
return 0;
fail:
if (huc_fw->load_status == INTEL_UC_FIRMWARE_PENDING)
huc_fw->load_status = INTEL_UC_FIRMWARE_FAIL;
DRM_ERROR("Failed to complete HuC uCode load with ret %d\n", err);
return err;
}
/**
* intel_huc_fini() - clean up resources allocated for HuC
* @dev_priv: the drm_i915_private device
*
* Cleans up by releasing the huc firmware GEM obj.
*/
void intel_huc_fini(struct drm_i915_private *dev_priv)
{
struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
mutex_lock(&dev_priv->drm.struct_mutex);
if (huc_fw->obj)
i915_gem_object_put(huc_fw->obj);
huc_fw->obj = NULL;
mutex_unlock(&dev_priv->drm.struct_mutex);
huc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE;
}
...@@ -176,6 +176,13 @@ struct intel_guc { ...@@ -176,6 +176,13 @@ struct intel_guc {
struct mutex send_mutex; struct mutex send_mutex;
}; };
struct intel_huc {
/* Generic uC firmware management */
struct intel_uc_fw fw;
/* HuC-specific additions */
};
/* intel_uc.c */ /* intel_uc.c */
void intel_uc_init_early(struct drm_i915_private *dev_priv); void intel_uc_init_early(struct drm_i915_private *dev_priv);
int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len); int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len);
...@@ -188,6 +195,8 @@ extern void intel_guc_fini(struct drm_i915_private *dev_priv); ...@@ -188,6 +195,8 @@ extern void intel_guc_fini(struct drm_i915_private *dev_priv);
extern const char *intel_uc_fw_status_repr(enum intel_uc_fw_status status); extern const char *intel_uc_fw_status_repr(enum intel_uc_fw_status status);
extern int intel_guc_suspend(struct drm_i915_private *dev_priv); extern int intel_guc_suspend(struct drm_i915_private *dev_priv);
extern int intel_guc_resume(struct drm_i915_private *dev_priv); extern int intel_guc_resume(struct drm_i915_private *dev_priv);
void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
struct intel_uc_fw *uc_fw);
u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv); u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv);
/* i915_guc_submission.c */ /* i915_guc_submission.c */
...@@ -213,4 +222,9 @@ static inline u32 guc_ggtt_offset(struct i915_vma *vma) ...@@ -213,4 +222,9 @@ static inline u32 guc_ggtt_offset(struct i915_vma *vma)
return offset; return offset;
} }
/* intel_huc.c */
void intel_huc_init(struct drm_i915_private *dev_priv);
void intel_huc_fini(struct drm_i915_private *dev_priv);
int intel_huc_load(struct drm_i915_private *dev_priv);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment