Matt Delco has uploaded this change for review.

View Change

drivers/intel/mipi_camera: add functionality

Some boards & variants are using raw asl files to define the ACPI devices
(and related properties) required by Linux MIPI camera drivers. These asl
files lack the SSDB property that's required by Windows MIPI camera
drivers. The mipi_camera driver can provide a SSDB property, but it's
missing some properties needed for Linux (and my scan of the source code
didn't find any current users of it).

This change updates the mipi_camera driver so it can provide a combined
superset of what's required by Linux and Windows. I've also expanded
the support so it has support for the IMGU and CIO2 devices.

BUG=none
BRANCH=none
TEST=Verified that the ACPI state generated by the driver (with
appropriate devicetree changes) is extremely comparible to what's
generated by the asl files. Verified that the camera still works in
CrOS on Atlas.

Change-Id: Ie9d84519eae1333eeb67107281a2deafc24e8a08
Signed-off-by: Matt Delco <delco@chromium.org>
---
M src/drivers/intel/mipi_camera/camera.c
M src/drivers/intel/mipi_camera/chip.h
M src/mainboard/google/poppy/dsdt.asl
3 files changed, 624 insertions(+), 40 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/63/31163/1
diff --git a/src/drivers/intel/mipi_camera/camera.c b/src/drivers/intel/mipi_camera/camera.c
index 0cea0ba..8844206 100644
--- a/src/drivers/intel/mipi_camera/camera.c
+++ b/src/drivers/intel/mipi_camera/camera.c
@@ -15,69 +15,599 @@

#include <arch/acpi.h>
#include <arch/acpi_device.h>
+#include <arch/acpi_pld.h>
#include <arch/acpigen.h>
#include <console/console.h>
#include <device/i2c_simple.h>
#include <device/device.h>
#include <device/path.h>
+#include <device/pci_def.h>
#include <string.h>
#include "chip.h"

+#define CIO2_PCI_DEV 0x14
+#define CIO2_PCI_FN 0x3
+
+/*
+ * This implementation assumes there is only 1 endpoint at each end of every
+ * data port. It also assumes there are no clock lanes. It also assumes
+ * that any VCM or NVM for a CAM is on the same I2C bus as the CAM.
+ */
+
+/*
+ * Adds settings for a CIO2 device (typically at "\_SB.PCI0.CIO2"). A _DSD is
+ * added that specifies a child table for each port (e.g., PRT0 for "port0"
+ * and PRT1 for "port1"). Each PRTx 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 EPx0 table). The EPx0 table
+ * primarily describes the # of lanes in "data-lines" and specifies the name
+ * of the other side in "remote-endpoint" (the name is usually of the form
+ * "\_SB.PCI0.I2Cx.CAM0" for the first port/cam and "\_SB.PCI0.I2Cx.CAM1"
+ * if there's a second port/cam).
+ */
+static void camera_fill_cio2(struct device *dev)
+{
+ struct drivers_intel_mipi_camera_config *config = dev->chip_info;
+ struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
+ char *ep_table_name[MAX_PORT_ENTRIES] = {NULL};
+ char *port_table_name[MAX_PORT_ENTRIES] = {NULL};
+ char *port_name[MAX_PORT_ENTRIES] = {NULL};
+ uint32_t i = 0;
+ char name[BUS_PATH_MAX];
+
+ if (!dsd)
+ return;
+
+ for (i = 0; i < config->cio2_num_ports; ++i) {
+ struct acpi_dp *ep_table = NULL;
+ struct acpi_dp *port_table = NULL;
+ snprintf(name, sizeof(name), "EP%u0", i);
+ ep_table_name[i] = strdup(name);
+ if (!ep_table_name[i])
+ break;
+ ep_table = acpi_dp_new_table(ep_table_name[i]);
+ if (!ep_table)
+ break;
+ acpi_dp_add_integer(ep_table, "endpoint", 0);
+ acpi_dp_add_integer(ep_table, "clock-lanes", 0);
+ if (config->cio2_lanes_used[i] > 0) {
+ struct acpi_dp *lanes = acpi_dp_new_table("data-lanes");
+ if (lanes) {
+ uint32_t j;
+ for (j = 1; j <= config->cio2_lanes_used[i];
+ ++j) {
+ acpi_dp_add_integer(lanes, NULL, j);
+ }
+ acpi_dp_add_array(ep_table, lanes);
+ }
+ }
+ if (config->cio2_lane_endpoint[i]) {
+ struct acpi_dp *remote =
+ acpi_dp_new_table("remote-endpoint");
+ if (remote) {
+ acpi_dp_add_reference(remote, NULL,
+ config->cio2_lane_endpoint[i]);
+ acpi_dp_add_integer(remote, NULL, 0);
+ acpi_dp_add_integer(remote, NULL, 0);
+ acpi_dp_add_array(ep_table, remote);
+ }
+ }
+ snprintf(name, sizeof(name), "PRT%u", i);
+ port_table_name[i] = strdup(name);
+ if (!port_table_name[i]) {
+ // acpi_dp_free(ep_table);
+ break;
+ }
+ port_table = acpi_dp_new_table(port_table_name[i]);
+ if (!port_table) {
+ // acpi_dp_free(ep_table);
+ break;
+ }
+ acpi_dp_add_integer(port_table, "port", i);
+ acpi_dp_add_child(port_table, "endpoint0", ep_table);
+
+ snprintf(name, sizeof(name), "port%u", i);
+ port_name[i] = strdup(name);
+ if (!port_name[i]) {
+ // acpi_dp_free(port_table);
+ break;
+ }
+ acpi_dp_add_child(dsd, port_name[i], port_table);
+ }
+ acpi_dp_write(dsd);
+
+ for (i = 0; i < config->cio2_num_ports; ++i) {
+ free(ep_table_name[i]);
+ free(port_table_name[i]);
+ free(port_name[i]);
+ }
+}
+
+/*
+ * 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(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;
+ uint16_t i2c_bus = dev->bus ? dev->bus->secondary : 0xFFFF;
+ uint16_t i2c_addr = dev->path.i2c.device;
+
+ if (config->use_pld && !config->disable_pld_defaults) {
+ if (!config->pld.ignore_color)
+ config->pld.ignore_color = 1;
+ if (!config->pld.visible)
+ config->pld.visible = 1;
+ if (!config->pld.vertical_offset)
+ config->pld.vertical_offset = 0xffff;
+ if (!config->pld.horizontal_offset)
+ config->pld.horizontal_offset = 0xffff;
+ /*
+ * PLD_PANEL_TOP has a value of zero, so the following
+ * will change any instance of PLD_PANEL_TOP to
+ * PLD_PANEL_FRONT unless disable_pld_defaults is set.
+ */
+ if (!config->pld.panel)
+ config->pld.panel = PLD_PANEL_FRONT;
+ /*
+ * PLD_HORIZONTAL_POSITION_LEFT has a value of zero, so
+ * the following will change any instance of that value
+ * to PLD_HORIZONTAL_POSITION_CENTER unless
+ * disable_pld_defaults is set.
+ */
+ if (!config->pld.horizontal_position) {
+ config->pld.horizontal_position =
+ PLD_HORIZONTAL_POSITION_CENTER;
+ }
+ /*
+ * The desired default for |vertical_position| is
+ * PLD_VERTICAL_POSITION_UPPER, which has a value of
+ * zero so no work is needed to set a default. The
+ * same applies for setting |shape| to PLD_SHAPE_ROUND.
+ */
+ }
+ if (config->use_pld)
+ acpigen_write_pld(&config->pld);
+
+ if (!config->disable_ssdb_defaults) {
+ if (!config->ssdb.bdf_value) {
+ config->ssdb.bdf_value = (CIO2_PCI_DEV << 3) |
+ CIO2_PCI_FN;
+ }
+ if (!config->ssdb.platform)
+ config->ssdb.platform = 9;
+ if (!config->ssdb.flash_support)
+ config->ssdb.flash_support = 2;
+ if (!config->ssdb.privacy_led)
+ config->ssdb.privacy_led = 1;
+ if (!config->ssdb.mipi_define)
+ config->ssdb.mipi_define = 1;
+ if (!config->ssdb.mclk_speed)
+ config->ssdb.mclk_speed = 19200000;
+ if (!config->ssdb.lanes_used) {
+ struct device *cio2 = pcidev_on_root(CIO2_PCI_DEV,
+ CIO2_PCI_FN);
+ struct drivers_intel_mipi_camera_config *cio2_config;
+ cio2_config = cio2 ? cio2->chip_info : NULL;
+ if (!cio2_config) {
+ printk(BIOS_ERR, "Failed to get CIO2 config\n");
+ } else if (cio2_config->device_type !=
+ INTEL_ACPI_CAMERA_CIO2) {
+ printk(BIOS_ERR, "Device type isn't CIO2: %u\n",
+ (u32)cio2_config->device_type);
+ } else if (config->ssdb.link_used >=
+ cio2_config->cio2_num_ports) {
+ printk(BIOS_ERR, "%u exceeds CIO2's %u links\n",
+ (u32)config->ssdb.link_used,
+ (u32)cio2_config->cio2_num_ports);
+ } else {
+ config->ssdb.lanes_used =
+ cio2_config->cio2_lanes_used[
+ config->ssdb.link_used];
+ }
+ }
+ }
+
+ /* Method (_DSM, 4, NotSerialized) */
+ acpigen_write_method("_DSM", 0x4);
+
+ /* ToBuffer (Arg0, Local0) */
+ acpigen_write_to_buffer(ARG0_OP, LOCAL0_OP);
+
+ /* If (LEqual (Local0, ToUUID(uuid))) */
+ acpigen_write_if();
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_uuid("822ace8f-2814-4174-a56b-5f029fe079ee");
+ acpigen_write_return_string(config->sensor_name ?
+ config->sensor_name : "UNKNOWN");
+ acpigen_pop_len(); /* If */
+
+ /* If (LEqual (Local0, ToUUID(uuid))) */
+ acpigen_write_if();
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_uuid("26257549-9271-4ca4-bb43-c4899d5a4881");
+ /* ToInteger (Arg2, Local1) */
+ acpigen_write_to_integer(ARG2_OP, LOCAL1_OP);
+
+ /* If (LEqual (Local1, 1)) */
+ acpigen_write_if_lequal_op_int(LOCAL1_OP, 1);
+ acpigen_write_return_integer((config->ssdb.vcm_type ? 1 : 0) +
+ (config->ssdb.rom_type ? 1 : 0) + 1);
+ acpigen_pop_len(); /* If Arg2=1 */
+
+ /* If (LEqual (Local1, 2)) */
+ acpigen_write_if_lequal_op_int(LOCAL1_OP, 2);
+ acpigen_write_return_integer(((uint32_t)i2c_bus) << 24 |
+ ((uint32_t)i2c_addr) << 8 |
+ 0x0); /* type 0 for sensor */
+ acpigen_pop_len(); /* If Arg2=2 */
+
+ if (config->ssdb.vcm_type) {
+ /* If (LEqual (Local1, 3)) */
+ acpigen_write_if_lequal_op_int(LOCAL1_OP, 3);
+ acpigen_write_return_integer(((uint32_t)i2c_bus) << 24 |
+ ((uint32_t)config->vcm_address)
+ << 8 |
+ 0x01); /* type 1 for vcm */
+ acpigen_pop_len(); /* If Arg2=3 */
+ }
+ if (config->ssdb.rom_type) {
+ /* If (LEqual (Local1, 3 or 4)) */
+ acpigen_write_if_lequal_op_int(LOCAL1_OP, 3 +
+ (config->ssdb.vcm_type ? 1 : 0));
+ acpigen_write_return_integer(((uint32_t)i2c_bus) << 24 |
+ ((uint32_t)config->rom_address)
+ << 8 |
+ 0x02); /* type 2 for rom */
+ acpigen_pop_len(); /* If Arg2=3 or 4 */
+ }
+
+ acpigen_pop_len(); /* If uuid */
+
+ /* Return (Buffer (One) { 0x0 }) */
+ acpigen_write_return_singleton_buffer(0x0);
+
+ acpigen_pop_len(); /* Method _DSM */
+
+ /* end of _dsm */
+
+ ep00 = acpi_dp_new_table("EP00");
+ if (ep00) {
+ struct acpi_dp *remote = NULL;
+ acpi_dp_add_integer(ep00, "endpoint", 0);
+ acpi_dp_add_integer(ep00, "clock-lanes", 0);
+ if (config->ssdb.lanes_used > 0) {
+ struct acpi_dp *lanes = acpi_dp_new_table("data-lanes");
+ if (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 > 0) {
+ struct acpi_dp *freq =
+ acpi_dp_new_table("link-frequencies");
+ if (freq) {
+ 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");
+ if (remote) {
+ const char *remote_name = NULL;
+ struct device *cio2 = NULL;
+ if (config->remote_name) {
+ remote_name = config->remote_name;
+ } else {
+ cio2 = pcidev_on_root(CIO2_PCI_DEV,
+ CIO2_PCI_FN);
+ if (cio2)
+ remote_name = acpi_device_path(cio2);
+ else
+ remote_name = "\\_SB.PCI0.CIO2";
+ }
+ acpi_dp_add_reference(remote, NULL, remote_name);
+ acpi_dp_add_integer(remote, NULL,
+ config->ssdb.link_used);
+ acpi_dp_add_integer(remote, NULL, 0); /* endpoint? */
+ acpi_dp_add_array(ep00, remote);
+ }
+ prt0 = acpi_dp_new_table("PRT0");
+ }
+ if (!prt0) {
+ // acpi_dp_free(ep00);
+ } else {
+ acpi_dp_add_integer(prt0, "port", 0);
+ acpi_dp_add_child(prt0, "endpoint0", ep00);
+ dsd = acpi_dp_new_table("_DSD");
+ }
+ if (!dsd) {
+ // acpi_dp_free(prt0);
+ } else {
+ const char *vcm_name = NULL;
+ 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)
+ acpi_dp_add_reference(dsd, "lens-focus", vcm_name);
+ 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(struct device *dev)
+{
+ struct drivers_intel_mipi_camera_config *config = dev->chip_info;
+ struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
+ if (!dsd)
+ return;
+
+ /* It might be possible to default size or width based on type. */
+ if (!config->disable_nvm_defaults && !config->nvm_pagesize)
+ config->nvm_pagesize = 1;
+ if (!config->disable_nvm_defaults && !config->nvm_readonly)
+ config->nvm_readonly = 1;
+
+ if (config->nvm_size)
+ acpi_dp_add_integer(dsd, "size", config->nvm_size);
+ if (config->nvm_pagesize)
+ acpi_dp_add_integer(dsd, "pagesize", config->nvm_pagesize);
+ if (config->nvm_readonly)
+ acpi_dp_add_integer(dsd, "read-only", config->nvm_readonly);
+ if (config->nvm_width)
+ acpi_dp_add_integer(dsd, "address-width", config->nvm_width);
+ acpi_dp_write(dsd);
+}
+
+static void camera_fill_vcm(struct device *dev)
+{
+ struct drivers_intel_mipi_camera_config *config = dev->chip_info;
+ struct acpi_dp *dsd;
+
+ if (!config->vcm_compat)
+ return;
+
+ dsd = acpi_dp_new_table("_DSD");
+ if (!dsd)
+ return;
+ acpi_dp_add_string(dsd, "compatible", config->vcm_compat);
+ acpi_dp_write(dsd);
+}
+
static void camera_fill_ssdt(struct device *dev)
{
struct drivers_intel_mipi_camera_config *config = dev->chip_info;
const char *scope = acpi_device_scope(dev);
- struct acpi_i2c i2c = {
- .address = dev->path.i2c.device,
- .mode_10bit = dev->path.i2c.mode_10bit,
- .speed = I2C_SPEED_FAST,
- .resource = scope,
- };

if (!dev->enabled || !scope)
return;

/* Device */
- acpigen_write_scope(scope);
- acpigen_write_device(acpi_device_name(dev));
- acpigen_write_name_string("_HID", config->acpi_hid);
- acpigen_write_name_integer("_UID", config->acpi_uid);
- acpigen_write_name_string("_DDN", config->chip_name);
- acpigen_write_STA(acpi_device_status(dev));
+ if (config->device_type == INTEL_ACPI_CAMERA_CIO2 ||
+ config->device_type == INTEL_ACPI_CAMERA_IMGU) {
+ u32 address;

- /* Resources */
- acpigen_write_name("_CRS");
- acpigen_write_resourcetemplate_header();
- acpi_device_write_i2c(&i2c);
- acpigen_write_resourcetemplate_footer();
+ if (dev->path.type != DEVICE_PATH_PCI) {
+ printk(BIOS_ERR, "CIO2/IMGU devices require PCI\n");
+ return;
+ }
+ acpigen_write_scope(scope);
+ acpigen_write_device(acpi_device_name(dev));
+ address = PCI_SLOT(dev->path.pci.devfn) & 0xffff;
+ address <<= 16;
+ address |= PCI_FUNC(dev->path.pci.devfn) & 0xffff;
+ acpigen_write_name_dword("_ADR", address);
+ acpigen_write_name_string("_DDN",
+ config->device_type == INTEL_ACPI_CAMERA_CIO2 ?
+ "Camera and Imaging Subsystem" : "Imaging Unit");
+ } else {
+ struct acpi_i2c i2c = {
+ .address = dev->path.i2c.device,
+ .mode_10bit = dev->path.i2c.mode_10bit,
+ .speed = I2C_SPEED_FAST,
+ .resource = scope,
+ };

- /* Mark it as Camera related device */
- acpigen_write_name_integer("CAMD", config->device_type);
+ if (dev->path.type != DEVICE_PATH_I2C) {
+ printk(BIOS_ERR, "Non-CIO2/IMGU devices require I2C\n");
+ return;
+ }

- /* Create Device specific data */
- if (config->device_type == INTEL_ACPI_CAMERA_SENSOR) {
- acpigen_write_method_serialized("SSDB", 0);
- acpigen_write_return_byte_buffer((uint8_t *)&config->ssdb,
- sizeof(config->ssdb));
- acpigen_pop_len(); /* Method */
+ acpigen_write_scope(scope);
+ acpigen_write_device(acpi_device_name(dev));
+ if (config->device_type == INTEL_ACPI_CAMERA_SENSOR)
+ acpigen_write_name_integer("_ADR", 0);
+ if (config->acpi_hid)
+ acpigen_write_name_string("_HID", config->acpi_hid);
+ else if (config->device_type == INTEL_ACPI_CAMERA_VCM)
+ acpigen_write_name_string("_HID",
+ ACPI_DT_NAMESPACE_HID);
+ else if (config->device_type == INTEL_ACPI_CAMERA_NVM)
+ acpigen_write_name_string("_HID", "INT3499");
+ acpigen_write_name_integer("_UID", config->acpi_uid);
+ acpigen_write_name_string("_DDN", config->chip_name);
+ acpigen_write_STA(acpi_device_status(dev));
+
+ /* Resources */
+ acpigen_write_name("_CRS");
+ acpigen_write_resourcetemplate_header();
+ acpi_device_write_i2c(&i2c);
+ /*
+ * The optional vcm/nvram devices are presumed to be on the
+ * same I2C bus as the camera sensor.
+ */
+ if (config->device_type == INTEL_ACPI_CAMERA_SENSOR &&
+ config->ssdb.vcm_type) {
+ struct acpi_i2c i2c_vcm = i2c;
+ i2c_vcm.address = config->vcm_address;
+ acpi_device_write_i2c(&i2c_vcm);
+ }
+ if (config->device_type == INTEL_ACPI_CAMERA_SENSOR &&
+ config->ssdb.rom_type) {
+ struct acpi_i2c i2c_rom = i2c;
+ i2c_rom.address = config->rom_address;
+ acpi_device_write_i2c(&i2c_rom);
+ }
+ acpigen_write_resourcetemplate_footer();
}

- /* Fill Power Sequencing Data */
- 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 */
+ /* Mark it as Camera related device */
+ if (config->device_type == INTEL_ACPI_CAMERA_CIO2 ||
+ config->device_type == INTEL_ACPI_CAMERA_IMGU ||
+ config->device_type == INTEL_ACPI_CAMERA_SENSOR ||
+ config->device_type == INTEL_ACPI_CAMERA_VCM) {
+ acpigen_write_name_integer("CAMD", config->device_type);
+ }
+
+ if (config->dep) {
+ acpigen_write_name("_DEP");
+ acpigen_write_package(1);
+ acpigen_emit_namestring(config->dep);
+ acpigen_pop_len();
+ }
+ if (config->pr0) {
+ acpigen_write_name("_PR0");
+ acpigen_write_package(1);
+ acpigen_emit_namestring(config->pr0);
+ acpigen_pop_len();
+ }
+ /* It's very unlikely you need _PR3 for any camera device */
+ if (config->pr3) {
+ acpigen_write_name("_PR3");
+ acpigen_write_package(1);
+ acpigen_emit_namestring(config->pr3);
+ acpigen_pop_len();
+ }
+
+ if (config->device_type == INTEL_ACPI_CAMERA_CIO2)
+ camera_fill_cio2(dev);
+ else if (config->device_type == INTEL_ACPI_CAMERA_SENSOR)
+ camera_fill_sensor(dev);
+ else if (config->device_type == INTEL_ACPI_CAMERA_NVM)
+ camera_fill_nvm(dev);
+ else if (config->device_type == INTEL_ACPI_CAMERA_VCM)
+ camera_fill_vcm(dev);

acpigen_pop_len(); /* Device */
acpigen_pop_len(); /* Scope */
- printk(BIOS_INFO, "%s: %s address 0%xh\n", acpi_device_path(dev),
- dev->chip_ops->name, dev->path.i2c.device);
+ if (dev->path.type == DEVICE_PATH_PCI) {
+ printk(BIOS_INFO, "%s: %s PCI address 0%x\n",
+ acpi_device_path(dev),
+ dev->chip_ops->name, dev->path.pci.devfn);
+ } else {
+ printk(BIOS_INFO, "%s: %s I2C address 0%xh\n",
+ acpi_device_path(dev),
+ dev->chip_ops->name, dev->path.i2c.device);
+ }
}

static const char *camera_acpi_name(const struct device *dev)
{
+ const char *prefix = NULL;
+ static char name[BUS_PATH_MAX];
struct drivers_intel_mipi_camera_config *config = dev->chip_info;
- return config->acpi_name;
+ if (config->acpi_name)
+ return config->acpi_name;
+ if (config->device_type == INTEL_ACPI_CAMERA_CIO2)
+ return "CIO2";
+ else if (config->device_type == INTEL_ACPI_CAMERA_IMGU)
+ return "IMGU";
+ else if (config->device_type == INTEL_ACPI_CAMERA_PMIC)
+ return "PMIC";
+
+ switch (config->device_type) {
+ case INTEL_ACPI_CAMERA_SENSOR:
+ prefix = "CAM";
+ break;
+ case INTEL_ACPI_CAMERA_VCM:
+ prefix = "VCM";
+ break;
+ case INTEL_ACPI_CAMERA_NVM:
+ prefix = "NVM";
+ break;
+ default:
+ prefix = "ERR"; /* Error */
+ break;
+ }
+ /*
+ * The cameras know which link # they use, so that's used
+ * as the basis for the instance #. The VCM and NVM don't
+ * have this information, so the best we can go on is the
+ * _UID.
+ */
+ snprintf(name, sizeof(name), "%s%u", prefix,
+ config->device_type == INTEL_ACPI_CAMERA_SENSOR ?
+ config->ssdb.link_used : config->acpi_uid);
+ return name;
}

static struct device_operations camera_ops = {
diff --git a/src/drivers/intel/mipi_camera/chip.h b/src/drivers/intel/mipi_camera/chip.h
index 741ae8e..50b757c 100644
--- a/src/drivers/intel/mipi_camera/chip.h
+++ b/src/drivers/intel/mipi_camera/chip.h
@@ -17,14 +17,19 @@
#define __INTEL_MIPI_CAMERA_CHIP_H__

#include <stdint.h>
+#include <arch/acpi_pld.h>

#define MAX_PWDB_ENTRIES 12
+#define MAX_PORT_ENTRIES 4
+#define MAX_LINK_FREQ_ENTRIES 4

enum intel_camera_device_type {
INTEL_ACPI_CAMERA_CIO2,
INTEL_ACPI_CAMERA_IMGU,
INTEL_ACPI_CAMERA_SENSOR,
INTEL_ACPI_CAMERA_VCM,
+ /* Value for NVM might might not be appropriate for CAMD */
+ INTEL_ACPI_CAMERA_NVM,
INTEL_ACPI_CAMERA_PMIC = 100,
};

@@ -77,12 +82,14 @@
uint8_t degree; /* Camera Orientation */
uint8_t mipi_define; /* MIPI info defined in ACPI or
sensor driver */
- uint32_t mclk; /* Clock info for sensor */
+ uint32_t mclk_speed; /* 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];
} __packed;

struct intel_pwdb {
@@ -95,14 +102,59 @@
} __packed;

struct drivers_intel_mipi_camera_config {
+ /* Generic settings */
+ enum intel_camera_device_type device_type;
+
+ /* Settings that should not need to be set since defaults sane. */
+ const char *acpi_name; /* defaults: CIO2,IMGU,PMIC,CAMx,VCMx,NVMx */
+
+ /*
+ * General settings that can be used for any type (but doesn't
+ * necessarily need to be used/specified for each device.
+ */
+ const char *chip_name;
+ const char *acpi_hid; /* vcm and nvm have defaults */
+ unsigned int acpi_uid;
+ const char *dep;
+ const char *pr0;
+ const char *pr3; /* _PR3 shouldn't be needed */
+
+ /* Settings specific to CIO2 device */
+ uint32_t cio2_num_ports;
+ uint32_t cio2_lanes_used[MAX_PORT_ENTRIES];
+ const char *cio2_lane_endpoint[MAX_PORT_ENTRIES];
+
+ /* Settings specific to camera sensor */
+ bool disable_ssdb_defaults;
struct intel_ssdb ssdb;
struct intel_pwdb pwdb[MAX_PWDB_ENTRIES];
- enum intel_camera_device_type device_type;
uint8_t num_pwdb_entries;
- const char *acpi_hid;
- const char *acpi_name;
- const char *chip_name;
- unsigned int acpi_uid;
+ 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 */
+ bool use_pld;
+ bool disable_pld_defaults;
+ struct acpi_pld pld;
+ uint16_t rom_address; /* I2C to use if ssdb.rom_type != 0 */
+ uint16_t vcm_address; /* I2C to use if ssdb.vcm_type != 0 */
+
+ /*
+ * Settings specific to nvram. Many values, if left as zero,
+ * will be assigned a default. Set disable_nvm_defaults to
+ * non-zero if you want to disable the defaulting behavior so
+ * you can use zero for a value.
+ */
+
+ bool disable_nvm_defaults;
+ uint32_t nvm_size;
+ uint32_t nvm_pagesize;
+ uint32_t nvm_readonly;
+ uint32_t nvm_width;
+
+ /* Settings specific to vcm */
+ const char *vcm_compat;
};

#endif
diff --git a/src/mainboard/google/poppy/dsdt.asl b/src/mainboard/google/poppy/dsdt.asl
index 0001867..d923ba5 100644
--- a/src/mainboard/google/poppy/dsdt.asl
+++ b/src/mainboard/google/poppy/dsdt.asl
@@ -39,8 +39,10 @@
{
Device (PCI0)
{
- /* Image processing unit */
+#if !IS_ENABLED(CONFIG_DRIVERS_INTEL_MIPI_CAMERA)
+ /* Image processing unit, if not using MIPI driver */
#include <soc/intel/skylake/acpi/ipu.asl>
+#endif
#include <soc/intel/skylake/acpi/systemagent.asl>
#include <soc/intel/skylake/acpi/pch.asl>
}

To view, visit change 31163. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: Ie9d84519eae1333eeb67107281a2deafc24e8a08
Gerrit-Change-Number: 31163
Gerrit-PatchSet: 1
Gerrit-Owner: Matt Delco <delco@chromium.org>
Gerrit-Reviewer: Matt Delco <delco@chromium.org>
Gerrit-Reviewer: Patrick Rudolph <siro@das-labor.org>
Gerrit-MessageType: newchange