Commit fbbd3f8f authored by Nicholas Kazlauskas's avatar Nicholas Kazlauskas Committed by Alex Deucher
Browse files

drm/amd/display: Add GPINT handler interface



[Why]
The General Purpose Interrupt is used on the DMCUB to pass lightweight
commands via a register to the DMCUB.

This is limited to 32-bit command and 32-bit response.

This will be used for shutting down the firmware in a clean manner.

[How]
Add the command IDs and the data register to correctly format
the commands.

Add the interface functions to dmub_srv for sending and receiving the
commands.

Signed-off-by: default avatarNicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Reviewed-by: default avatarTony Cheng <Tony.Cheng@amd.com>
Acked-by: default avatarBhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 47b0c91f
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 Advanced Micro Devices, Inc.
 *
 * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
 *
 * Authors: AMD
 *
 */

#ifndef _DMUB_GPINT_CMD_H_
#define _DMUB_GPINT_CMD_H_

#include "dmub_types.h"

/**
 * The register format for sending a command via the GPINT.
 */
union dmub_gpint_data_register {
	struct {
		uint32_t param : 16;
		uint32_t command_code : 12;
		uint32_t status : 4;
	} bits;
	uint32_t all;
};

/**
 * The shifts and masks below may alternatively be used to format and read
 * the command register bits.
 */

#define DMUB_GPINT_DATA_PARAM_MASK 0xFFFF
#define DMUB_GPINT_DATA_PARAM_SHIFT 0

#define DMUB_GPINT_DATA_COMMAND_CODE_MASK 0xFFF
#define DMUB_GPINT_DATA_COMMAND_CODE_SHIFT 16

#define DMUB_GPINT_DATA_STATUS_MASK 0xF
#define DMUB_GPINT_DATA_STATUS_SHIFT 28

/*
 * Command IDs should be treated as stable ABI.
 * Do not reuse or modify IDs.
 */

enum dmub_gpint_command {
	DMUB_GPINT__INVALID_COMMAND = 0,
	DMUB_GPINT__GET_FW_VERSION = 1,
	DMUB_GPINT__STOP_FW = 2,
};

#endif /* _DMUB_GPINT_CMD_H_ */
+48 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@

#include "dmub_types.h"
#include "dmub_cmd.h"
#include "dmub_gpint_cmd.h"
#include "dmub_rb.h"

#if defined(__cplusplus)
@@ -262,6 +263,14 @@ struct dmub_srv_hw_funcs {
	bool (*is_phy_init)(struct dmub_srv *dmub);

	bool (*is_auto_load_done)(struct dmub_srv *dmub);

	void (*set_gpint)(struct dmub_srv *dmub,
			  union dmub_gpint_data_register reg);

	bool (*is_gpint_acked)(struct dmub_srv *dmub,
			       union dmub_gpint_data_register reg);

	uint32_t (*get_gpint_response)(struct dmub_srv *dmub);
};

/**
@@ -516,6 +525,45 @@ enum dmub_status dmub_srv_wait_for_phy_init(struct dmub_srv *dmub,
enum dmub_status dmub_srv_wait_for_idle(struct dmub_srv *dmub,
					uint32_t timeout_us);

/**
 * dmub_srv_send_gpint_command() - Sends a GPINT based command.
 * @dmub: the dmub service
 * @command_code: the command code to send
 * @param: the command parameter to send
 * @timeout_us: the maximum number of microseconds to wait
 *
 * Sends a command via the general purpose interrupt (GPINT).
 * Waits for the number of microseconds specified by timeout_us
 * for the command ACK before returning.
 *
 * Can be called after software initialization.
 *
 * Return:
 *   DMUB_STATUS_OK - success
 *   DMUB_STATUS_TIMEOUT - wait for ACK timed out
 *   DMUB_STATUS_INVALID - unspecified error
 */
enum dmub_status
dmub_srv_send_gpint_command(struct dmub_srv *dmub,
			    enum dmub_gpint_command command_code,
			    uint16_t param, uint32_t timeout_us);

/**
 * dmub_srv_get_gpint_response() - Queries the GPINT response.
 * @dmub: the dmub service
 * @response: the response for the last GPINT
 *
 * Returns the response code for the last GPINT interrupt.
 *
 * Can be called after software initialization.
 *
 * Return:
 *   DMUB_STATUS_OK - success
 *   DMUB_STATUS_INVALID - unspecified error
 */
enum dmub_status dmub_srv_get_gpint_response(struct dmub_srv *dmub,
					     uint32_t *response);

#if defined(__cplusplus)
}
#endif
+22 −0
Original line number Diff line number Diff line
@@ -217,3 +217,25 @@ bool dmub_dcn20_is_supported(struct dmub_srv *dmub)

	return supported;
}

void dmub_dcn20_set_gpint(struct dmub_srv *dmub,
			  union dmub_gpint_data_register reg)
{
	REG_WRITE(DMCUB_GPINT_DATAIN1, reg.all);
}

bool dmub_dcn20_is_gpint_acked(struct dmub_srv *dmub,
			       union dmub_gpint_data_register reg)
{
	union dmub_gpint_data_register test;

	reg.bits.status = 0;
	test.all = REG_READ(DMCUB_GPINT_DATAIN1);

	return test.all == reg.all;
}

uint32_t dmub_dcn20_get_gpint_response(struct dmub_srv *dmub)
{
	return REG_READ(DMCUB_SCRATCH7);
}
+9 −0
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ struct dmub_srv;
	DMUB_SR(DMCUB_SCRATCH13) \
	DMUB_SR(DMCUB_SCRATCH14) \
	DMUB_SR(DMCUB_SCRATCH15) \
	DMUB_SR(DMCUB_GPINT_DATAIN1) \
	DMUB_SR(CC_DC_PIPE_DIS) \
	DMUB_SR(MMHUBBUB_SOFT_RESET) \
	DMUB_SR(DCN_VM_FB_LOCATION_BASE) \
@@ -183,4 +184,12 @@ bool dmub_dcn20_is_hw_init(struct dmub_srv *dmub);

bool dmub_dcn20_is_supported(struct dmub_srv *dmub);

void dmub_dcn20_set_gpint(struct dmub_srv *dmub,
			  union dmub_gpint_data_register reg);

bool dmub_dcn20_is_gpint_acked(struct dmub_srv *dmub,
			       union dmub_gpint_data_register reg);

uint32_t dmub_dcn20_get_gpint_response(struct dmub_srv *dmub);

#endif /* _DMUB_DCN20_H_ */
+50 −0
Original line number Diff line number Diff line
@@ -126,6 +126,9 @@ static bool dmub_srv_hw_setup(struct dmub_srv *dmub, enum dmub_asic asic)
		funcs->set_inbox1_wptr = dmub_dcn20_set_inbox1_wptr;
		funcs->is_supported = dmub_dcn20_is_supported;
		funcs->is_hw_init = dmub_dcn20_is_hw_init;
		funcs->set_gpint = dmub_dcn20_set_gpint;
		funcs->is_gpint_acked = dmub_dcn20_is_gpint_acked;
		funcs->get_gpint_response = dmub_dcn20_get_gpint_response;

		if (asic == DMUB_ASIC_DCN21) {
			dmub->regs = &dmub_srv_dcn21_regs;
@@ -522,3 +525,50 @@ enum dmub_status dmub_srv_wait_for_idle(struct dmub_srv *dmub,

	return DMUB_STATUS_TIMEOUT;
}

enum dmub_status
dmub_srv_send_gpint_command(struct dmub_srv *dmub,
			    enum dmub_gpint_command command_code,
			    uint16_t param, uint32_t timeout_us)
{
	union dmub_gpint_data_register reg;
	uint32_t i;

	if (!dmub->sw_init)
		return DMUB_STATUS_INVALID;

	if (!dmub->hw_funcs.set_gpint)
		return DMUB_STATUS_INVALID;

	if (!dmub->hw_funcs.is_gpint_acked)
		return DMUB_STATUS_INVALID;

	reg.bits.status = 1;
	reg.bits.command_code = command_code;
	reg.bits.param = param;

	dmub->hw_funcs.set_gpint(dmub, reg);

	for (i = 0; i < timeout_us; ++i) {
		if (dmub->hw_funcs.is_gpint_acked(dmub, reg))
			return DMUB_STATUS_OK;
	}

	return DMUB_STATUS_TIMEOUT;
}

enum dmub_status dmub_srv_get_gpint_response(struct dmub_srv *dmub,
					     uint32_t *response)
{
	*response = 0;

	if (!dmub->sw_init)
		return DMUB_STATUS_INVALID;

	if (!dmub->hw_funcs.get_gpint_response)
		return DMUB_STATUS_INVALID;

	*response = dmub->hw_funcs.get_gpint_response(dmub);

	return DMUB_STATUS_OK;
}