/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef __ACPI_ACPI_PLD_H__
#define __ACPI_ACPI_PLD_H__

#include <acpi/acpi.h>
#include <stdint.h>

enum acpi_pld_panel {
	PLD_PANEL_TOP,
	PLD_PANEL_BOTTOM,
	PLD_PANEL_LEFT,
	PLD_PANEL_RIGHT,
	PLD_PANEL_FRONT,
	PLD_PANEL_BACK,
	PLD_PANEL_UNKNOWN
};

enum acpi_pld_vertical_position {
	PLD_VERTICAL_POSITION_UPPER,
	PLD_VERTICAL_POSITION_CENTER,
	PLD_VERTICAL_POSITION_LOWER
};

/*
 * The ACPI spec 6.2A does not define the horizontal position field.
 * These values are taken from the IASL compiler:
 * https://github.com/acpica/acpica/blob/master/source/components/utilities/utglobal.c#L321
 */

enum acpi_pld_horizontal_position {
	PLD_HORIZONTAL_POSITION_LEFT,
	PLD_HORIZONTAL_POSITION_CENTER,
	PLD_HORIZONTAL_POSITION_RIGHT
};

enum acpi_pld_shape {
	PLD_SHAPE_ROUND,
	PLD_SHAPE_OVAL,
	PLD_SHAPE_SQUARE,
	PLD_SHAPE_VERTICAL_RECTANGLE,
	PLD_SHAPE_HORIZONTAL_RECTANGLE,
	PLD_SHAPE_VERTICAL_TRAPEZOID,
	PLD_SHAPE_HORIZONTAL_TRAPEZOID,
	PLD_SHAPE_UNKNOWN,
	PLD_SHAPE_CHAMFERED
};

enum acpi_pld_orientation {
	PLD_ORIENTATION_HORIZONTAL,
	PLD_ORIENTATION_VERTICAL,
};

enum acpi_pld_rotate {
	PLD_ROTATE_0,
	PLD_ROTATE_45,
	PLD_ROTATE_90,
	PLD_ROTATE_135,
	PLD_ROTATE_180,
	PLD_ROTATE_225,
	PLD_ROTATE_270,
	PLD_ROTATE_315
};

#define ACPI_PLD_GROUP(__token, __position)	\
	{					\
		.token = __token,		\
		.position = __position,		\
	}

/*
 * ACPI specification 6.3 third paragraph of section 6.1.8:
 * All Panel references (Top, Bottom, Right, Left, etc.) are interpreted
 * as though the user is facing the front of the system.
 *
 * A `_PLD` describes the offset and rotation of a single device connection point
 * from an `origin` that resides in the lower left hand corner of its Panel.
 */

#define ACPI_PLD_TYPE_A(__panel, __horiz, __grp)				\
	{									\
		.visible = true,						\
		.panel = PLD_PANEL_##__panel,					\
		.shape = PLD_SHAPE_HORIZONTAL_RECTANGLE,			\
		.horizontal_position = PLD_HORIZONTAL_POSITION_##__horiz,	\
		.group = __grp,							\
	}

#define ACPI_PLD_TYPE_C(__panel, __horiz, __grp)				\
	{									\
		.visible = true,						\
		.panel = PLD_PANEL_##__panel,					\
		.shape = PLD_SHAPE_OVAL,					\
		.horizontal_position = PLD_HORIZONTAL_POSITION_##__horiz,	\
		.group = __grp,							\
	}

struct acpi_pld_group {
	uint8_t token;
	uint8_t position;
};

struct acpi_pld {
	/* Color field can be explicitly ignored */
	bool ignore_color;
	uint8_t color_red;
	uint8_t color_blue;
	uint8_t color_green;

	/* Port characteristics */
	bool visible;		/* Can be seen by the user */
	bool lid;		/* Port is on lid of device */
	bool dock;		/* Port is in a docking station */
	bool bay;		/* Port is in a bay */
	bool ejectable;		/* Device is ejectable, has _EJx objects */
	bool ejectable_ospm;	/* Device needs OSPM to eject */
	uint16_t width;		/* Width in mm */
	uint16_t height;	/* Height in mm */
	uint16_t vertical_offset;
	uint16_t horizontal_offset;
	enum acpi_pld_panel panel;
	enum acpi_pld_horizontal_position horizontal_position;
	enum acpi_pld_vertical_position vertical_position;
	enum acpi_pld_shape shape;
	enum acpi_pld_rotate rotation;

	/* Port grouping */
	enum acpi_pld_orientation orientation;
	struct acpi_pld_group group;
	uint8_t draw_order;
	uint8_t cabinet_number;
	uint8_t card_cage_number;

	/* Set if this PLD defines a reference shape */
	bool reference_shape;
};

/* Fill out PLD structure with defaults based on USB port type */
int acpi_pld_fill_usb(struct acpi_pld *pld, enum acpi_upc_type type,
		      struct acpi_pld_group *group);

/* Turn PLD structure into a 20 byte ACPI buffer */
int acpi_pld_to_buffer(const struct acpi_pld *pld, uint8_t *buf, int buf_len);

#endif /* __ACPI_ACPI_PLD_H__ */