Re: [PATCH 20/31] drm/i915/slpc: Add parameter set/unset/get, task control/status functions

Michal Wajdeczko
2017-09-21T13:47:09Z

On Tue, 19 Sep 2017 19:41:56 +0200, Sagar Arun Kamble
<sagar.a.kamble@intel.com> wrote:

> SLPC behavior can be changed through set of parameters.
> These parameters can be updated and queried from i915 though
> Host to GuC SLPC events. This patch adds parameter update
> events for setting/unsetting/getting parameters. SLPC has
> various tasks for controlling different controls. This patch
> adds functions to control and query the task status.
>
> v1: Use host2guc_slpc
> update slcp_param_id enum values for SLPC 2015.2.4
> return void instead of ignored error code (Paulo)
>
> v2: Checkpatch update.
>
> v3: Rebase.
>
> v4: Updated with GuC firmware v9.
>
> v5: Updated input structure to host2guc_slpc. Added functions
> to update only parameters in the SLPC shared memory. This
> will allow to setup shared data with all parameters and send
> single event to SLPC take them into effect. Commit message
> update. (Sagar)
>
> v6: Rearranged helpers to use them in slpc_shared_data_init.
> Added definition of SLPC_KMD_MAX_PARAM.
>
> v7: Added definition of host2guc_slpc with rearrangement of patches.
> Added task control/status functions.
>
> v8: Rebase w.r.t s/intel_guc_send/intel_guc_send_mmio.
>
> Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
> Signed-off-by: Tom O'Rourke <Tom.O'Rourke@intel.com>
> Signed-off-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
> ---
> drivers/gpu/drm/i915/intel_guc.c | 21 ++++-
> drivers/gpu/drm/i915/intel_guc.h | 2 +
> drivers/gpu/drm/i915/intel_slpc.c | 185
> ++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/i915/intel_slpc.h | 8 ++
> 4 files changed, 215 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_guc.c
> b/drivers/gpu/drm/i915/intel_guc.c
> index a92c7e8..656bae9 100644
> --- a/drivers/gpu/drm/i915/intel_guc.c
> +++ b/drivers/gpu/drm/i915/intel_guc.c
> @@ -67,9 +67,11 @@ void intel_guc_init_send_regs(struct intel_guc *guc)
> /*
> * This function implements the MMIO based host to GuC interface.
> */
> -int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32
> len)
> +int __intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32
> len,
> + u32 *output)
> {
> struct drm_i915_private *dev_priv = guc_to_i915(guc);
> + union slpc_event_output_header header;

Don't pollute generic send function with slpc specific code.

> u32 status;
> int i;
> int ret;
> @@ -115,12 +117,29 @@ int intel_guc_send_mmio(struct intel_guc *guc,
> const u32 *action, u32 len)
> action[0], ret, status, I915_READ(SOFT_SCRATCH(15)));
> }
> + /*
> + * Output data from Host to GuC SLPC actions is populated in scratch
> + * registers SOFT_SCRATCH(1) to SOFT_SCRATCH(14) based on event.

Note that receiving more data over MMIO will be handled by these pending
patches
https://patchwork.freedesktop.org/patch/170667/
https://patchwork.freedesktop.org/patch/170669/

The same series will also add support for responses over CT so stay tuned!

> + * Currently only SLPC action status in GuC is meaningful as Host
> + * can query only overridden parameters and that are fetched from
> + * Host-GuC SLPC shared data.
> + */
> + if (output && !ret) {
> + output[0] = header.value = I915_READ(SOFT_SCRATCH(1));
> + ret = header.status;
> + }
> +
> intel_uncore_forcewake_put(dev_priv, guc->send_regs.fw_domains);
> mutex_unlock(&guc->send_mutex);
> return ret;
> }
> +int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32
> len)
> +{
> + return __intel_guc_send_mmio(guc, action, len, NULL);
> +}
> +
> int intel_guc_sample_forcewake(struct intel_guc *guc)
> {
> struct drm_i915_private *dev_priv = guc_to_i915(guc);
> diff --git a/drivers/gpu/drm/i915/intel_guc.h
> b/drivers/gpu/drm/i915/intel_guc.h
> index b835d30..c27d2dd 100644
> --- a/drivers/gpu/drm/i915/intel_guc.h
> +++ b/drivers/gpu/drm/i915/intel_guc.h
> @@ -132,6 +132,8 @@ struct intel_guc {
> int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32
> len);
> void gen8_guc_raise_irq(struct intel_guc *guc);
> void intel_guc_init_send_regs(struct intel_guc *guc);
> +int __intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32
> len,
> + u32 *output);
> int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32
> len);
> int intel_guc_sample_forcewake(struct intel_guc *guc);
> int intel_guc_runtime_suspend(struct intel_guc *guc);
> diff --git a/drivers/gpu/drm/i915/intel_slpc.c
> b/drivers/gpu/drm/i915/intel_slpc.c
> index 73e7bf5..f47d81e 100644
> --- a/drivers/gpu/drm/i915/intel_slpc.c
> +++ b/drivers/gpu/drm/i915/intel_slpc.c
> @@ -132,6 +132,191 @@ int slpc_mem_task_control(struct slpc_shared_data
> *data, u64 val,
> return ret;
> }
> +static void host2guc_slpc(struct intel_slpc *slpc,
> + struct slpc_event_input *input, u32 len)
> +{
> + struct intel_guc *guc = slpc_to_guc(slpc);
> + u32 *data;
> + u32 output[SLPC_EVENT_MAX_OUTPUT_ARGS];
> + int ret = 0;
> +
> + /*
> + * We have only 15 scratch registers for communication.
> + * the first we will use for the event ID in input and
> + * output data. Event processing status will be present
> + * in SOFT_SCRATCH(1) register.
> + */
> + BUILD_BUG_ON(SLPC_EVENT_MAX_INPUT_ARGS > 14);
> + BUILD_BUG_ON(SLPC_EVENT_MAX_OUTPUT_ARGS < 1);
> + BUILD_BUG_ON(SLPC_EVENT_MAX_OUTPUT_ARGS > 14);
> +
> + data = (u32 *) input;
> + data[0] = INTEL_GUC_ACTION_SLPC_REQUEST;
> + ret = __intel_guc_send_mmio(guc, data, len, output);
> +
> + if (ret)
> + DRM_ERROR("event 0x%x status %d\n",
> + ((output[0] & 0xFF00) >> 8), ret);
> +}
> +
> +static void host2guc_slpc_set_param(struct intel_slpc *slpc,
> + u32 id, u32 value)
> +{
> + struct slpc_event_input data = {0};
> +
> + data.header.value = SLPC_EVENT(SLPC_EVENT_PARAMETER_SET, 2);
> + data.args[0] = id;
> + data.args[1] = value;
> +
> + host2guc_slpc(slpc, &data, 4);
> +}
> +
> +static void host2guc_slpc_unset_param(struct intel_slpc *slpc,
> + u32 id)
> +{
> + struct slpc_event_input data = {0};
> +
> + data.header.value = SLPC_EVENT(SLPC_EVENT_PARAMETER_UNSET, 1);
> + data.args[0] = id;
> +
> + host2guc_slpc(slpc, &data, 3);
> +}
> +
> +void intel_slpc_set_param(struct intel_slpc *slpc,
> + u32 id,
> + u32 value)
> +{
> + struct page *page;
> + struct slpc_shared_data *data = NULL;
> +
> + WARN_ON(id >= SLPC_MAX_PARAM);
> +
> + if (!slpc->vma)
> + return;
> +
> + page = i915_vma_first_page(slpc->vma);
> + data = kmap_atomic(page);
> + slpc_mem_set_param(data, id, value);
> + kunmap_atomic(data);
> +
> + host2guc_slpc_set_param(slpc, id, value);
> +}
> +
> +void intel_slpc_unset_param(struct intel_slpc *slpc,
> + u32 id)
> +{
> + struct page *page;
> + struct slpc_shared_data *data = NULL;
> +
> + WARN_ON(id >= SLPC_MAX_PARAM);
> +
> + if (!slpc->vma)
> + return;
> +
> + page = i915_vma_first_page(slpc->vma);
> + data = kmap_atomic(page);
> + slpc_mem_unset_param(data, id);
> + kunmap_atomic(data);
> +
> + host2guc_slpc_unset_param(slpc, id);
> +}
> +
> +void intel_slpc_get_param(struct intel_slpc *slpc,
> + u32 id,
> + int *overriding, u32 *value)
> +{
> + struct page *page;
> + struct slpc_shared_data *data = NULL;
> + u32 bits;
> +
> + WARN_ON(id >= SLPC_MAX_PARAM);
> +
> + if (!slpc->vma)
> + return;
> +
> + page = i915_vma_first_page(slpc->vma);
> + data = kmap_atomic(page);
> + if (overriding) {
> + bits = data->override_parameters_set_bits[id >> 5];
> + *overriding = (0 != (bits & (1 << (id % 32))));
> + }
> + if (value)
> + *value = data->override_parameters_values[id];
> +
> + kunmap_atomic(data);
> +}
> +
> +int intel_slpc_task_control(struct intel_slpc *slpc, u64 val,
> + u32 enable_id, u32 disable_id)
> +{
> + struct drm_i915_private *dev_priv = slpc_to_i915(slpc);
> + int ret = 0;
> +
> + if (!slpc->active)
> + return -ENODEV;
> +
> + intel_runtime_pm_get(dev_priv);
> +
> + if (val == SLPC_PARAM_TASK_DEFAULT) {
> + /* set default */
> + intel_slpc_unset_param(slpc, enable_id);
> + intel_slpc_unset_param(slpc, disable_id);
> + } else if (val == SLPC_PARAM_TASK_ENABLED) {
> + /* set enable */
> + intel_slpc_set_param(slpc, enable_id, 1);
> + intel_slpc_unset_param(slpc, disable_id);
> + } else if (val == SLPC_PARAM_TASK_DISABLED) {
> + /* set disable */
> + intel_slpc_set_param(slpc, disable_id, 1);
> + intel_slpc_unset_param(slpc, enable_id);
> + } else {
> + ret = -EINVAL;
> + }
> +
> + intel_slpc_enable(slpc);
> + intel_runtime_pm_put(dev_priv);
> +
> + return ret;
> +}
> +
> +int intel_slpc_task_status(struct intel_slpc *slpc, u64 *val,
> + u32 enable_id, u32 disable_id)
> +{
> + int override_enable, override_disable;
> + u32 value_enable, value_disable;
> + int ret = 0;
> +
> + if (!slpc->active) {
> + ret = -ENODEV;
> + } else if (val) {
> + intel_slpc_get_param(slpc, enable_id, &override_enable,
> + &value_enable);
> + intel_slpc_get_param(slpc, disable_id, &override_disable,
> + &value_disable);
> +
> + /*
> + * Set the output value:
> + * 0: default
> + * 1: enabled
> + * 2: disabled
> + * 3: unknown (should not happen)
> + */
> + if (override_disable && (value_disable == 1))
> + *val = SLPC_PARAM_TASK_DISABLED;
> + else if (override_enable && (value_enable == 1))
> + *val = SLPC_PARAM_TASK_ENABLED;
> + else if (!override_enable && !override_disable)
> + *val = SLPC_PARAM_TASK_DEFAULT;
> + else
> + *val = SLPC_PARAM_TASK_UNKNOWN;
> +
> + } else {
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> static void slpc_shared_data_init(struct intel_slpc *slpc)
> {
> struct drm_i915_private *dev_priv = slpc_to_i915(slpc);
> diff --git a/drivers/gpu/drm/i915/intel_slpc.h
> b/drivers/gpu/drm/i915/intel_slpc.h
> index 9312b2f..0ff17f0 100644
> --- a/drivers/gpu/drm/i915/intel_slpc.h
> +++ b/drivers/gpu/drm/i915/intel_slpc.h
> @@ -247,6 +247,14 @@ struct slpc_param {
> #define SLPC_PARAM_TASK_UNKNOWN 3
> /* intel_slpc.c */
> +void intel_slpc_set_param(struct intel_slpc *slpc, u32 id, u32 value);
> +void intel_slpc_unset_param(struct intel_slpc *slpc, u32 id);
> +void intel_slpc_get_param(struct intel_slpc *slpc, u32 id,
> + int *overriding, u32 *value);
> +int intel_slpc_task_control(struct intel_slpc *slpc, u64 val,
> + u32 enable_id, u32 disable_id);
> +int intel_slpc_task_status(struct intel_slpc *slpc, u64 *val,
> + u32 enable_id, u32 disable_id);
> void intel_slpc_init(struct intel_slpc *slpc);
> void intel_slpc_cleanup(struct intel_slpc *slpc);
> void intel_slpc_enable(struct intel_slpc *slpc);