drivers/intel/mipi_camera: Add support for camera sensor in SSDT

This change updates mipi_camera driver to handle camera sensor.

Change-Id: I581c9bf9b87eac69e88ec11724c3b26ee5fa9431
Signed-off-by: Sugnan Prabhu S <sugnan.prabhu.s@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/42467
Reviewed-by: Rizwan Qureshi <rizwan.qureshi@intel.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/drivers/intel/mipi_camera/camera.c b/src/drivers/intel/mipi_camera/camera.c
index b1075b8..5230a2a 100644
--- a/src/drivers/intel/mipi_camera/camera.c
+++ b/src/drivers/intel/mipi_camera/camera.c
@@ -10,6 +10,144 @@
 #include <device/pci_def.h>
 #include "chip.h"
 
+#define SENSOR_NAME_UUID	"822ace8f-2814-4174-a56b-5f029fe079ee"
+#define SENSOR_TYPE_UUID	"26257549-9271-4ca4-bb43-c4899d5a4881"
+#define DEFAULT_ENDPOINT	0
+
+static void camera_fill_sensor_defaults(struct drivers_intel_mipi_camera_config *config)
+{
+	if (config->disable_ssdb_defaults)
+		return;
+
+	if (!config->ssdb.platform)
+		config->ssdb.platform = PLATFORM_SKC;
+
+	if (!config->ssdb.flash_support)
+		config->ssdb.flash_support = FLASH_DISABLE;
+
+	if (!config->ssdb.privacy_led)
+		config->ssdb.privacy_led = PRIVACY_LED_A_16mA;
+
+	if (!config->ssdb.mipi_define)
+		config->ssdb.mipi_define = MIPI_INFO_ACPI_DEFINED;
+
+	if (!config->ssdb.mclk_speed)
+		config->ssdb.mclk_speed = CLK_FREQ_19_2MHZ;
+}
+
+/*
+ * Adds settings for a camera sensor device (typically at "\_SB.PCI0.I2Cx.CAMy"). The drivers
+ * for Linux tends to expect the camera sensor device and any related nvram / vcm devices to be
+ * separate ACPI devices, while the drivers for Windows want all of these to be grouped
+ * together in the camera sensor ACPI device. This implementation tries to satisfy both,
+ * though the unfortunate tradeoff is that the same I2C address for nvram and vcm is advertised
+ * by multiple devices in ACPI (via "_CRS"). The Windows driver can use the "_DSM" method to
+ * disambiguate the I2C resources in the camera sensor ACPI device.  Drivers for Windows
+ * typically query "SSDB" for configuration information (represented as a binary blob dump of
+ * struct), while Linux drivers typically consult individual parameters in "_DSD".
+ *
+ * The tree of tables in "_DSD" is analogous to what's used for the "CIO2" device.  The _DSD
+ * specifies a child table for the sensor's port (e.g., PRT0 for "port0"--this implementation
+ * assumes a camera only has 1 port). The PRT0 table specifies a table for each endpoint
+ * (though only 1 endpoint is supported by this implementation so the table only has an
+ * "endpoint0" that points to a EP00 table). The EP00 table primarily describes the # of lanes
+ * in "data-lines", a list of frequencies in "list-frequencies", and specifies the name of the
+ * other side in "remote-endpoint" (typically "\_SB.PCI0.CIO2").
+ */
+static void camera_fill_sensor(const struct device *dev)
+{
+	struct drivers_intel_mipi_camera_config *config = dev->chip_info;
+	struct acpi_dp *ep00 = NULL;
+	struct acpi_dp *prt0 = NULL;
+	struct acpi_dp *dsd = NULL;
+	struct acpi_dp *remote = NULL;
+	const char *vcm_name = NULL;
+	struct acpi_dp *lens_focus = NULL;
+
+	camera_fill_sensor_defaults(config);
+
+	ep00 = acpi_dp_new_table("EP00");
+	acpi_dp_add_integer(ep00, "endpoint", DEFAULT_ENDPOINT);
+	acpi_dp_add_integer(ep00, "clock-lanes", 0);
+
+	if (config->ssdb.lanes_used > 0) {
+		struct acpi_dp *lanes = acpi_dp_new_table("data-lanes");
+		uint32_t i;
+		for (i = 1; i <= config->ssdb.lanes_used; ++i)
+			acpi_dp_add_integer(lanes, NULL, i);
+		acpi_dp_add_array(ep00, lanes);
+	}
+
+	if (config->num_freq_entries) {
+		struct acpi_dp *freq = acpi_dp_new_table("link-frequencies");
+		uint32_t i;
+		for (i = 0; i < config->num_freq_entries && i < MAX_LINK_FREQ_ENTRIES; ++i)
+			acpi_dp_add_integer(freq, NULL, config->link_freq[i]);
+		acpi_dp_add_array(ep00, freq);
+	}
+
+	remote = acpi_dp_new_table("remote-endpoint");
+
+	acpi_dp_add_reference(remote, NULL, config->remote_name);
+	acpi_dp_add_integer(remote, NULL, config->ssdb.link_used);
+	acpi_dp_add_integer(remote, NULL, DEFAULT_ENDPOINT);
+	acpi_dp_add_array(ep00, remote);
+
+	prt0 = acpi_dp_new_table("PRT0");
+
+	acpi_dp_add_integer(prt0, "port", 0);
+	acpi_dp_add_child(prt0, "endpoint0", ep00);
+	dsd = acpi_dp_new_table("_DSD");
+
+	acpi_dp_add_integer(dsd, "clock-frequency", config->ssdb.mclk_speed);
+
+	if (config->ssdb.degree)
+		acpi_dp_add_integer(dsd, "rotation", 180);
+
+	if (config->ssdb.vcm_type) {
+		if (config->vcm_name) {
+			vcm_name = config->vcm_name;
+		} else {
+			const struct device_path path = {
+				.type = DEVICE_PATH_I2C,
+				.i2c.device = config->vcm_address,
+			};
+			struct device *vcm_dev = find_dev_path(dev->bus, &path);
+			struct drivers_intel_mipi_camera_config *vcm_config;
+			vcm_config = vcm_dev ? vcm_dev->chip_info : NULL;
+
+			if (!vcm_config)
+				printk(BIOS_ERR, "Failed to get VCM\n");
+			else if (vcm_config->device_type != INTEL_ACPI_CAMERA_VCM)
+				printk(BIOS_ERR, "Device isn't VCM\n");
+			else
+				vcm_name = acpi_device_path(vcm_dev);
+		}
+	}
+
+	if (vcm_name) {
+		lens_focus = acpi_dp_new_table("lens-focus");
+		acpi_dp_add_reference(lens_focus, NULL, vcm_name);
+		acpi_dp_add_array(dsd, lens_focus);
+	}
+
+	acpi_dp_add_child(dsd, "port0", prt0);
+	acpi_dp_write(dsd);
+
+	acpigen_write_method_serialized("SSDB", 0);
+	acpigen_write_return_byte_buffer((uint8_t *)&config->ssdb, sizeof(config->ssdb));
+	acpigen_pop_len(); /* Method */
+
+	/* Fill Power Sequencing Data */
+	if (config->num_pwdb_entries > 0) {
+		acpigen_write_method_serialized("PWDB", 0);
+		acpigen_write_return_byte_buffer((uint8_t *)&config->pwdb,
+						 sizeof(struct intel_pwdb) *
+						 config->num_pwdb_entries);
+		acpigen_pop_len(); /* Method */
+	}
+}
+
 static void camera_fill_nvm(const struct device *dev)
 {
 	struct drivers_intel_mipi_camera_config *config = dev->chip_info;
@@ -135,6 +273,9 @@
 	}
 
 	switch (config->device_type) {
+	case INTEL_ACPI_CAMERA_SENSOR:
+		camera_fill_sensor(dev);
+		break;
 	case INTEL_ACPI_CAMERA_VCM:
 		camera_fill_vcm(dev);
 		break;
diff --git a/src/drivers/intel/mipi_camera/chip.h b/src/drivers/intel/mipi_camera/chip.h
index b04a100..c962c9a 100644
--- a/src/drivers/intel/mipi_camera/chip.h
+++ b/src/drivers/intel/mipi_camera/chip.h
@@ -5,7 +5,41 @@
 
 #include <stdint.h>
 
-#define MAX_PWDB_ENTRIES 12
+#define DEFAULT_LINK_FREQ	450000000
+#define MAX_PWDB_ENTRIES	12
+#define MAX_PORT_ENTRIES	4
+#define MAX_LINK_FREQ_ENTRIES	4
+
+enum camera_device_type {
+	DEV_TYPE_SENSOR = 0,
+	DEV_TYPE_VCM,
+	DEV_TYPE_ROM
+};
+
+enum intel_camera_platform_type {
+	PLATFORM_SKC = 9,
+	PLATFORM_CNL = 10
+};
+
+enum intel_camera_flash_type {
+	FLASH_DEFAULT = 0,
+	FLASH_DISABLE = 2,
+	FLASH_ENABLE = 3
+};
+
+enum intel_camera_led_type {
+	PRIVACY_LED_DEFAULT = 0,
+	PRIVACY_LED_A_16mA
+};
+
+enum intel_camera_mipi_info {
+	MIPI_INFO_SENSOR_DRIVER = 0,
+	MIPI_INFO_ACPI_DEFINED
+};
+
+#define CLK_FREQ_19_2MHZ	19200000
+#define CLK_FREQ_24MHZ		24000000
+#define CLK_FREQ_20MHZ		20000000
 
 enum intel_camera_device_type {
 	INTEL_ACPI_CAMERA_CIO2,
@@ -65,12 +99,16 @@
 	uint8_t degree;				/* Camera Orientation */
 	uint8_t mipi_define;			/* MIPI info defined in ACPI or
 						sensor driver */
+	uint32_t mclk_speed;			/* Clock info for sensor */
 	uint32_t mclk;				/* Clock info for sensor */
 	uint8_t control_logic_id;		/* PMIC device node used for
 						the camera sensor */
 	uint8_t mipi_data_format;		/* MIPI data format */
 	uint8_t silicon_version;		/* Silicon version */
 	uint8_t customer_id;			/* Customer ID */
+	uint8_t mclk_port;
+	uint8_t reserved[13];			/* Pads SSDB out so the binary blob in ACPI is
+						   the same size as seen on other firmwares.*/
 } __packed;
 
 struct intel_pwdb {
@@ -91,6 +129,13 @@
 	const char *acpi_name;
 	const char *chip_name;
 	unsigned int acpi_uid;
+
+	/* Settings specific to camera sensor */
+	bool disable_ssdb_defaults;
+
+	uint8_t num_freq_entries;	/* # of elements in link_freq */
+	uint32_t link_freq[MAX_LINK_FREQ_ENTRIES];
+	const char *sensor_name;	/* default "UNKNOWN" */
 	const char *remote_name;	/* default "\_SB.PCI0.CIO2" */
 	const char *vcm_name;		/* defaults to |vcm_address| device */
 	uint16_t rom_address;		/* I2C to use if ssdb.rom_type != 0 */