/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <acpi/acpi.h>
#include <acpi/acpigen.h>
#include <string.h>
#include "i915.h"

void
drivers_intel_gma_displays_ssdt_generate(const struct i915_gpu_controller_info *conf)
{
	size_t i;
	const char *names[] = { "UNK", "VGA", "TV", "DVI", "LCD" };
	int counters[ARRAY_SIZE(names)] = { 0 };

	if (!conf->ndid)
		return;

	acpigen_write_scope("\\_SB.PCI0.GFX0");

	/*
	  Method (_DOD, 0)
	  {
		Return (Package() {
				0x5a5a5a5a,
				0x5a5a5a5a,
				0x5a5a5a5a
			})
	  }
	*/
	acpigen_write_method("_DOD", 0);

	acpigen_emit_byte(RETURN_OP);
	acpigen_write_package(conf->ndid);
	for (i = 0; i < conf->ndid; i++) {
		acpigen_write_dword (conf->did[i] | 0x80010000);
	}
	acpigen_pop_len(); /* End Package. */

	acpigen_pop_len(); /* End Method. */

	for (i = 0; i < conf->ndid; i++) {
		char name[5];
		int kind;

		kind = (conf->did[i] >> 8) & 0xf;
		if (kind >= ARRAY_SIZE(names)) {
			kind = 0;
		}

		snprintf(name, sizeof(name), "%s%d", names[kind], counters[kind]);
		counters[kind]++;

		/* Device (LCD0) */
		acpigen_write_device(name);

		/* Name (_ADR, 0x0410) */
		acpigen_write_name_dword("_ADR", conf->did[i] & 0xffff);

		/* ACPI brightness for LCD.  */
		if (kind == 4) {
			/*
			  Method (_BCL, 0, NotSerialized)
			  {
				Return (^^XBCL())
			  }
			*/
			acpigen_write_method("_BCL", 0);
			acpigen_emit_byte(RETURN_OP);
			acpigen_emit_namestring("^^XBCL");
			acpigen_pop_len();

			/*
			  Method (_BCM, 1, NotSerialized)
			  {
				^^XBCM(Arg0)
			  }
			*/
			acpigen_write_method("_BCM", 1);
			acpigen_emit_namestring("^^XBCM");
			acpigen_emit_byte(ARG0_OP);
			acpigen_pop_len();

			/*
			  Method (_BQC, 0, NotSerialized)
			  {
				Return (^^XBQC())
			  }
			*/
			acpigen_write_method("_BQC", 0);
			acpigen_emit_byte(RETURN_OP);
			acpigen_emit_namestring("^^XBQC");
			acpigen_pop_len();
		}

		/*
		 * _DCS, _DGS and _DSS are required by specification. However,
		 * we never implemented them properly, and no OS driver com-
		 * plained yet. So we stub them out and keep the traditional
		 * behavior in case an OS driver checks for their existence.
		 */

		/*
		  Method(_DCS, 0)
		  {
			Return (0x1d)
		  }
		*/
		acpigen_write_method("_DCS", 0);
		acpigen_write_return_integer(0x1d);
		acpigen_pop_len();

		/*
		  Method(_DGS, 0)
		  {
			Return (0)
		  }
		*/
		acpigen_write_method("_DGS", 0);
		acpigen_write_return_integer(0);
		acpigen_pop_len();

		/*
		  Method(_DSS, 1)
		  {
		  }
		*/
		acpigen_write_method("_DSS", 1);
		acpigen_pop_len();

		acpigen_pop_len(); /* End Device. */
	}

	acpigen_pop_len(); /* End Scope. */
}