diff options
Diffstat (limited to 'drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c')
-rw-r--r-- | drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c new file mode 100644 index 000000000000..5b4506a71126 --- /dev/null +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -0,0 +1,758 @@ +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/dmi.h> +#include <linux/efi.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <media/v4l2-subdev.h> +#include <linux/mfd/intel_soc_pmic.h> +#include "../../include/linux/vlv2_plat_clock.h" +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include "../../include/linux/atomisp_platform.h" +#include "../../include/linux/atomisp_gmin_platform.h" + +#define MAX_SUBDEVS 8 + +/* Should be defined in vlv2_plat_clock API, isn't: */ +#define VLV2_CLK_PLL_19P2MHZ 1 +#define VLV2_CLK_XTAL_19P2MHZ 0 +#define VLV2_CLK_ON 1 +#define VLV2_CLK_OFF 2 +#define ELDO1_SEL_REG 0x19 +#define ELDO1_1P8V 0x16 +#define ELDO1_CTRL_SHIFT 0x00 +#define ELDO2_SEL_REG 0x1a +#define ELDO2_1P8V 0x16 +#define ELDO2_CTRL_SHIFT 0x01 + +struct gmin_subdev { + struct v4l2_subdev *subdev; + int clock_num; + int clock_src; + struct gpio_desc *gpio0; + struct gpio_desc *gpio1; + struct regulator *v1p8_reg; + struct regulator *v2p8_reg; + struct regulator *v1p2_reg; + struct regulator *v2p8_vcm_reg; + enum atomisp_camera_port csi_port; + unsigned int csi_lanes; + enum atomisp_input_format csi_fmt; + enum atomisp_bayer_order csi_bayer; + bool v1p8_on; + bool v2p8_on; + bool v1p2_on; + bool v2p8_vcm_on; +}; + +static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS]; + +static enum { PMIC_UNSET = 0, PMIC_REGULATOR, PMIC_AXP, PMIC_TI , + PMIC_CRYSTALCOVE } pmic_id; + +/* The atomisp uses type==0 for the end-of-list marker, so leave space. */ +static struct intel_v4l2_subdev_table pdata_subdevs[MAX_SUBDEVS + 1]; + +static const struct atomisp_platform_data pdata = { + .subdevs = pdata_subdevs, +}; + +/* + * Something of a hack. The ECS E7 board drives camera 2.8v from an + * external regulator instead of the PMIC. There's a gmin_CamV2P8 + * config variable that specifies the GPIO to handle this particular + * case, but this needs a broader architecture for handling camera + * power. + */ +enum { V2P8_GPIO_UNSET = -2, V2P8_GPIO_NONE = -1 }; +static int v2p8_gpio = V2P8_GPIO_UNSET; + +/* + * Something of a hack. The CHT RVP board drives camera 1.8v from an + * external regulator instead of the PMIC just like ECS E7 board, see the + * comments above. + */ +enum { V1P8_GPIO_UNSET = -2, V1P8_GPIO_NONE = -1 }; +static int v1p8_gpio = V1P8_GPIO_UNSET; + +static LIST_HEAD(vcm_devices); +static DEFINE_MUTEX(vcm_lock); + +static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev); + +/* + * Legacy/stub behavior copied from upstream platform_camera.c. The + * atomisp driver relies on these values being non-NULL in a few + * places, even though they are hard-coded in all current + * implementations. + */ +const struct atomisp_camera_caps *atomisp_get_default_camera_caps(void) +{ + static const struct atomisp_camera_caps caps = { + .sensor_num = 1, + .sensor = { + { .stream_num = 1, }, + }, + }; + return ∩︀ +} +EXPORT_SYMBOL_GPL(atomisp_get_default_camera_caps); + +const struct atomisp_platform_data *atomisp_get_platform_data(void) +{ + return &pdata; +} +EXPORT_SYMBOL_GPL(atomisp_get_platform_data); + +static int af_power_ctrl(struct v4l2_subdev *subdev, int flag) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + + if (gs && gs->v2p8_vcm_on == flag) + return 0; + gs->v2p8_vcm_on = flag; + + /* + * The power here is used for dw9817, + * regulator is from rear sensor + */ + if (gs->v2p8_vcm_reg) { + if (flag) + return regulator_enable(gs->v2p8_vcm_reg); + else + return regulator_disable(gs->v2p8_vcm_reg); + } + return 0; +} + +/* + * Used in a handful of modules. Focus motor control, I think. Note + * that there is no configurability in the API, so this needs to be + * fixed where it is used. + * + * struct camera_af_platform_data { + * int (*power_ctrl)(struct v4l2_subdev *subdev, int flag); + * }; + * + * Note that the implementation in MCG platform_camera.c is stubbed + * out anyway (i.e. returns zero from the callback) on BYT. So + * neither needed on gmin platforms or supported upstream. + */ +const struct camera_af_platform_data *camera_get_af_platform_data(void) +{ + static struct camera_af_platform_data afpd = { + .power_ctrl = af_power_ctrl, + }; + return &afpd; +} +EXPORT_SYMBOL_GPL(camera_get_af_platform_data); + +int atomisp_register_i2c_module(struct v4l2_subdev *subdev, + struct camera_sensor_platform_data *plat_data, + enum intel_v4l2_subdev_type type) +{ + int i; + struct i2c_board_info *bi; + struct gmin_subdev *gs; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct acpi_device *adev; + + dev_info(&client->dev, "register atomisp i2c module type %d\n", type); + + /* The windows driver model (and thus most BIOSes by default) + * uses ACPI runtime power management for camera devices, but + * we don't. Disable it, or else the rails will be needlessly + * tickled during suspend/resume. This has caused power and + * performance issues on multiple devices. */ + adev = ACPI_COMPANION(&client->dev); + if (adev) + adev->power.flags.power_resources = 0; + + for (i=0; i < MAX_SUBDEVS; i++) + if (!pdata.subdevs[i].type) + break; + + if (pdata.subdevs[i].type) + return -ENOMEM; + + /* Note subtlety of initialization order: at the point where + * this registration API gets called, the platform data + * callbacks have probably already been invoked, so the + * gmin_subdev struct is already initialized for us. */ + gs = find_gmin_subdev(subdev); + + pdata.subdevs[i].type = type; + pdata.subdevs[i].port = gs->csi_port; + pdata.subdevs[i].subdev = subdev; + pdata.subdevs[i].v4l2_subdev.i2c_adapter_id = client->adapter->nr; + + /* Convert i2c_client to i2c_board_info */ + bi = &pdata.subdevs[i].v4l2_subdev.board_info; + memcpy(bi->type, client->name, I2C_NAME_SIZE); + bi->flags = client->flags; + bi->addr = client->addr; + bi->irq = client->irq; + bi->platform_data = plat_data; + + return 0; +} +EXPORT_SYMBOL_GPL(atomisp_register_i2c_module); + +struct v4l2_subdev *atomisp_gmin_find_subdev(struct i2c_adapter *adapter, + struct i2c_board_info *board_info) +{ + int i; + for (i=0; i < MAX_SUBDEVS && pdata.subdevs[i].type; i++) { + struct intel_v4l2_subdev_table *sd = &pdata.subdevs[i]; + if (sd->v4l2_subdev.i2c_adapter_id == adapter->nr && + sd->v4l2_subdev.board_info.addr == board_info->addr) + return sd->subdev; + } + return NULL; +} +EXPORT_SYMBOL_GPL(atomisp_gmin_find_subdev); + +int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd) +{ + int i, j; + + if (!sd) + return 0; + + for (i = 0; i < MAX_SUBDEVS; i++) { + if (pdata.subdevs[i].subdev == sd) { + for (j = i + 1; j <= MAX_SUBDEVS; j++) + pdata.subdevs[j - 1] = pdata.subdevs[j]; + } + if (gmin_subdevs[i].subdev == sd) { + if (gmin_subdevs[i].gpio0) + gpiod_put(gmin_subdevs[i].gpio0); + gmin_subdevs[i].gpio0 = NULL; + if (gmin_subdevs[i].gpio1) + gpiod_put(gmin_subdevs[i].gpio1); + gmin_subdevs[i].gpio1 = NULL; + if (pmic_id == PMIC_REGULATOR) { + regulator_put(gmin_subdevs[i].v1p8_reg); + regulator_put(gmin_subdevs[i].v2p8_reg); + regulator_put(gmin_subdevs[i].v1p2_reg); + regulator_put(gmin_subdevs[i].v2p8_vcm_reg); + } + gmin_subdevs[i].subdev = NULL; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(atomisp_gmin_remove_subdev); + +struct gmin_cfg_var { + const char *name, *val; +}; + +static const struct gmin_cfg_var ffrd8_vars[] = { + { "INTCF1B:00_ImxId", "0x134" }, + { "INTCF1B:00_CsiPort", "1" }, + { "INTCF1B:00_CsiLanes", "4" }, + { "INTCF1B:00_CamClk", "0" }, + {}, +}; + +/* Cribbed from MCG defaults in the mt9m114 driver, not actually verified + * vs. T100 hardware */ +static const struct gmin_cfg_var t100_vars[] = { + { "INT33F0:00_CsiPort", "0" }, + { "INT33F0:00_CsiLanes", "1" }, + { "INT33F0:00_CamClk", "1" }, + {}, +}; + +static const struct gmin_cfg_var mrd7_vars[] = { + {"INT33F8:00_CamType", "1"}, + {"INT33F8:00_CsiPort", "1"}, + {"INT33F8:00_CsiLanes","2"}, + {"INT33F8:00_CsiFmt","13"}, + {"INT33F8:00_CsiBayer", "0"}, + {"INT33F8:00_CamClk", "0"}, + {"INT33F9:00_CamType", "1"}, + {"INT33F9:00_CsiPort", "0"}, + {"INT33F9:00_CsiLanes","1"}, + {"INT33F9:00_CsiFmt","13"}, + {"INT33F9:00_CsiBayer", "0"}, + {"INT33F9:00_CamClk", "1"}, + {}, +}; + +static const struct gmin_cfg_var ecs7_vars[] = { + {"INT33BE:00_CsiPort", "1"}, + {"INT33BE:00_CsiLanes","2"}, + {"INT33BE:00_CsiFmt","13"}, + {"INT33BE:00_CsiBayer", "2"}, + {"INT33BE:00_CamClk", "0"}, + {"INT33F0:00_CsiPort", "0"}, + {"INT33F0:00_CsiLanes","1"}, + {"INT33F0:00_CsiFmt","13"}, + {"INT33F0:00_CsiBayer", "0"}, + {"INT33F0:00_CamClk", "1"}, + {"gmin_V2P8GPIO","402"}, + {}, +}; + + +static const struct gmin_cfg_var i8880_vars[] = { + {"XXOV2680:00_CsiPort", "1"}, + {"XXOV2680:00_CsiLanes","1"}, + {"XXOV2680:00_CamClk","0"}, + {"XXGC0310:00_CsiPort", "0"}, + {"XXGC0310:00_CsiLanes", "1"}, + {"XXGC0310:00_CamClk", "1"}, + {}, +}; + +static const struct { + const char *dmi_board_name; + const struct gmin_cfg_var *vars; +} hard_vars[] = { + { "BYT-T FFD8", ffrd8_vars }, + { "T100TA", t100_vars }, + { "MRD7", mrd7_vars }, + { "ST70408", ecs7_vars }, + { "VTA0803", i8880_vars }, +}; + + +#define GMIN_CFG_VAR_EFI_GUID EFI_GUID(0xecb54cd9, 0xe5ae, 0x4fdc, \ + 0xa9, 0x71, 0xe8, 0x77, \ + 0x75, 0x60, 0x68, 0xf7) + +#define CFG_VAR_NAME_MAX 64 + +static int gmin_platform_init(struct i2c_client *client) +{ + return 0; +} + +static int gmin_platform_deinit(void) +{ + return 0; +} + +static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) +{ + int i, ret; + struct device *dev; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + + if (!pmic_id) { + + pmic_id = PMIC_REGULATOR; + } + + if (!client) + return NULL; + + dev = &client->dev; + + for (i=0; i < MAX_SUBDEVS && gmin_subdevs[i].subdev; i++) + ; + if (i >= MAX_SUBDEVS) + return NULL; + + dev_info(dev, + "gmin: initializing atomisp module subdev data.PMIC ID %d\n", + pmic_id); + + gmin_subdevs[i].subdev = subdev; + gmin_subdevs[i].clock_num = gmin_get_var_int(dev, "CamClk", 0); + /*WA:CHT requires XTAL clock as PLL is not stable.*/ + gmin_subdevs[i].clock_src = gmin_get_var_int(dev, "ClkSrc", + VLV2_CLK_PLL_19P2MHZ); + gmin_subdevs[i].csi_port = gmin_get_var_int(dev, "CsiPort", 0); + gmin_subdevs[i].csi_lanes = gmin_get_var_int(dev, "CsiLanes", 1); + gmin_subdevs[i].gpio0 = gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW); + gmin_subdevs[i].gpio1 = gpiod_get_index(dev, NULL, 1, GPIOD_OUT_LOW); + + if (!IS_ERR(gmin_subdevs[i].gpio0)) { + ret = gpiod_direction_output(gmin_subdevs[i].gpio0, 0); + if (ret) + dev_err(dev, "gpio0 set output failed: %d\n", ret); + } else { + gmin_subdevs[i].gpio0 = NULL; + } + + if (!IS_ERR(gmin_subdevs[i].gpio1)) { + ret = gpiod_direction_output(gmin_subdevs[i].gpio1, 0); + if (ret) + dev_err(dev, "gpio1 set output failed: %d\n", ret); + } else { + gmin_subdevs[i].gpio1 = NULL; + } + + if (pmic_id == PMIC_REGULATOR) { + gmin_subdevs[i].v1p8_reg = regulator_get(dev, "V1P8SX"); + gmin_subdevs[i].v2p8_reg = regulator_get(dev, "V2P8SX"); + gmin_subdevs[i].v1p2_reg = regulator_get(dev, "V1P2A"); + gmin_subdevs[i].v2p8_vcm_reg = regulator_get(dev, "VPROG4B"); + + /* Note: ideally we would initialize v[12]p8_on to the + * output of regulator_is_enabled(), but sadly that + * API is broken with the current drivers, returning + * "1" for a regulator that will then emit a + * "unbalanced disable" WARNing if we try to disable + * it. */ + } + + return &gmin_subdevs[i]; +} + +static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev) +{ + int i; + for (i=0; i < MAX_SUBDEVS; i++) + if (gmin_subdevs[i].subdev == subdev) + return &gmin_subdevs[i]; + return gmin_subdev_add(subdev); +} + +static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + if (gs && gs->gpio0) { + gpiod_set_value(gs->gpio0, on); + return 0; + } + return -EINVAL; +} + +static int gmin_gpio1_ctrl(struct v4l2_subdev *subdev, int on) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + if (gs && gs->gpio1) { + gpiod_set_value(gs->gpio1, on); + return 0; + } + return -EINVAL; +} + +int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + + if (gs && gs->v1p2_on == on) + return 0; + gs->v1p2_on = on; + + if (gs->v1p2_reg) { + if (on) + return regulator_enable(gs->v1p2_reg); + else + return regulator_disable(gs->v1p2_reg); + } + + /*TODO:v1p2 needs to extend to other PMICs*/ + + return -EINVAL; +} +int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + int ret; + + if (v1p8_gpio == V1P8_GPIO_UNSET) { + v1p8_gpio = gmin_get_var_int(NULL, "V1P8GPIO", V1P8_GPIO_NONE); + if (v1p8_gpio != V1P8_GPIO_NONE) { + pr_info("atomisp_gmin_platform: 1.8v power on GPIO %d\n", + v1p8_gpio); + ret = gpio_request(v1p8_gpio, "camera_v1p8_en"); + if (!ret) + ret = gpio_direction_output(v1p8_gpio, 0); + if (ret) + pr_err("V1P8 GPIO initialization failed\n"); + } + } + + if (gs && gs->v1p8_on == on) + return 0; + gs->v1p8_on = on; + + if (v1p8_gpio >= 0) + gpio_set_value(v1p8_gpio, on); + + if (gs->v1p8_reg) { + regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000); + if (on) + return regulator_enable(gs->v1p8_reg); + else + return regulator_disable(gs->v1p8_reg); + } + + return -EINVAL; +} + +int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + int ret; + + if (v2p8_gpio == V2P8_GPIO_UNSET) { + v2p8_gpio = gmin_get_var_int(NULL, "V2P8GPIO", V2P8_GPIO_NONE); + if (v2p8_gpio != V2P8_GPIO_NONE) { + pr_info("atomisp_gmin_platform: 2.8v power on GPIO %d\n", + v2p8_gpio); + ret = gpio_request(v2p8_gpio, "camera_v2p8"); + if (!ret) + ret = gpio_direction_output(v2p8_gpio, 0); + if (ret) + pr_err("V2P8 GPIO initialization failed\n"); + } + } + + if (gs && gs->v2p8_on == on) + return 0; + gs->v2p8_on = on; + + if (v2p8_gpio >= 0) + gpio_set_value(v2p8_gpio, on); + + if (gs->v2p8_reg) { + regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000); + if (on) + return regulator_enable(gs->v2p8_reg); + else + return regulator_disable(gs->v2p8_reg); + } + + return -EINVAL; +} + +int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on) +{ + int ret = 0; + struct gmin_subdev *gs = find_gmin_subdev(subdev); + if (on) + ret = vlv2_plat_set_clock_freq(gs->clock_num, gs->clock_src); + if (ret) + return ret; + return vlv2_plat_configure_clock(gs->clock_num, + on ? VLV2_CLK_ON : VLV2_CLK_OFF); +} + +static int gmin_csi_cfg(struct v4l2_subdev *sd, int flag) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct gmin_subdev *gs = find_gmin_subdev(sd); + + if (!client || !gs) + return -ENODEV; + + return camera_sensor_csi(sd, gs->csi_port, gs->csi_lanes, + gs->csi_fmt, gs->csi_bayer, flag); +} + +static struct camera_vcm_control *gmin_get_vcm_ctrl(struct v4l2_subdev *subdev, + char *camera_module) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct gmin_subdev *gs = find_gmin_subdev(subdev); + struct camera_vcm_control *vcm; + + if (client == NULL || gs == NULL) + return NULL; + + if (!camera_module) + return NULL; + + mutex_lock(&vcm_lock); + list_for_each_entry(vcm, &vcm_devices, list) { + if (!strcmp(camera_module, vcm->camera_module)) { + mutex_unlock(&vcm_lock); + return vcm; + } + } + + mutex_unlock(&vcm_lock); + return NULL; +} + +static struct camera_sensor_platform_data gmin_plat = { + .gpio0_ctrl = gmin_gpio0_ctrl, + .gpio1_ctrl = gmin_gpio1_ctrl, + .v1p8_ctrl = gmin_v1p8_ctrl, + .v2p8_ctrl = gmin_v2p8_ctrl, + .v1p2_ctrl = gmin_v1p2_ctrl, + .flisclk_ctrl = gmin_flisclk_ctrl, + .platform_init = gmin_platform_init, + .platform_deinit = gmin_platform_deinit, + .csi_cfg = gmin_csi_cfg, + .get_vcm_ctrl = gmin_get_vcm_ctrl, +}; + +struct camera_sensor_platform_data *gmin_camera_platform_data( + struct v4l2_subdev *subdev, + enum atomisp_input_format csi_format, + enum atomisp_bayer_order csi_bayer) +{ + struct gmin_subdev *gs = find_gmin_subdev(subdev); + gs->csi_fmt = csi_format; + gs->csi_bayer = csi_bayer; + + return &gmin_plat; +} +EXPORT_SYMBOL_GPL(gmin_camera_platform_data); + +int atomisp_gmin_register_vcm_control(struct camera_vcm_control *vcmCtrl) +{ + if (!vcmCtrl) + return -EINVAL; + + mutex_lock(&vcm_lock); + list_add_tail(&vcmCtrl->list, &vcm_devices); + mutex_unlock(&vcm_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(atomisp_gmin_register_vcm_control); + +/* Retrieves a device-specific configuration variable. The dev + * argument should be a device with an ACPI companion, as all + * configuration is based on firmware ID. */ +int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t *out_len) +{ + char var8[CFG_VAR_NAME_MAX]; + efi_char16_t var16[CFG_VAR_NAME_MAX]; + struct efivar_entry *ev; + u32 efiattr_dummy; + int i, j, ret; + unsigned long efilen; + + if (dev && ACPI_COMPANION(dev)) + dev = &ACPI_COMPANION(dev)->dev; + + if (dev) + ret = snprintf(var8, sizeof(var8), "%s_%s", dev_name(dev), var); + else + ret = snprintf(var8, sizeof(var8), "gmin_%s", var); + + if (ret < 0 || ret >= sizeof(var8) - 1) + return -EINVAL; + + /* First check a hard-coded list of board-specific variables. + * Some device firmwares lack the ability to set EFI variables at + * runtime. */ + for (i = 0; i < ARRAY_SIZE(hard_vars); i++) { + if (dmi_match(DMI_BOARD_NAME, hard_vars[i].dmi_board_name)) { + for (j = 0; hard_vars[i].vars[j].name; j++) { + size_t vl; + const struct gmin_cfg_var *gv; + + gv = &hard_vars[i].vars[j]; + vl = strlen(gv->val); + + if (strcmp(var8, gv->name)) + continue; + if (vl > *out_len - 1) + return -ENOSPC; + + memcpy(out, gv->val, min(*out_len, vl+1)); + out[*out_len-1] = 0; + *out_len = vl; + + return 0; + } + } + } + + /* Our variable names are ASCII by construction, but EFI names + * are wide chars. Convert and zero-pad. */ + memset(var16, 0, sizeof(var16)); + for (i = 0; i < sizeof(var8) && var8[i]; i++) + var16[i] = var8[i]; + + /* To avoid owerflows when calling the efivar API */ + if (*out_len > ULONG_MAX) + return -EINVAL; + + /* Not sure this API usage is kosher; efivar_entry_get()'s + * implementation simply uses VariableName and VendorGuid from + * the struct and ignores the rest, but it seems like there + * ought to be an "official" efivar_entry registered + * somewhere? */ + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + memcpy(&ev->var.VariableName, var16, sizeof(var16)); + ev->var.VendorGuid = GMIN_CFG_VAR_EFI_GUID; + + efilen = *out_len; + ret = efivar_entry_get(ev, &efiattr_dummy, &efilen, out); + + kfree(ev); + *out_len = efilen; + + if (ret) + dev_warn(dev, "Failed to find gmin variable %s\n", var8); + + return ret; +} +EXPORT_SYMBOL_GPL(gmin_get_config_var); + +int gmin_get_var_int(struct device *dev, const char *var, int def) +{ + char val[CFG_VAR_NAME_MAX]; + size_t len = sizeof(val); + long result; + int ret; + + ret = gmin_get_config_var(dev, var, val, &len); + if (!ret) { + val[len] = 0; + ret = kstrtol(val, 0, &result); + } + + return ret ? def : result; +} +EXPORT_SYMBOL_GPL(gmin_get_var_int); + +int camera_sensor_csi(struct v4l2_subdev *sd, u32 port, + u32 lanes, u32 format, u32 bayer_order, int flag) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct camera_mipi_info *csi = NULL; + + if (flag) { + csi = kzalloc(sizeof(*csi), GFP_KERNEL); + if (!csi) { + dev_err(&client->dev, "out of memory\n"); + return -ENOMEM; + } + csi->port = port; + csi->num_lanes = lanes; + csi->input_format = format; + csi->raw_bayer_order = bayer_order; + v4l2_set_subdev_hostdata(sd, (void *)csi); + csi->metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED; + csi->metadata_effective_width = NULL; + dev_info(&client->dev, + "camera pdata: port: %d lanes: %d order: %8.8x\n", + port, lanes, bayer_order); + } else { + csi = v4l2_get_subdev_hostdata(sd); + kfree(csi); + } + + return 0; +} +EXPORT_SYMBOL_GPL(camera_sensor_csi); + +/* PCI quirk: The BYT ISP advertises PCI runtime PM but it doesn't + * work. Disable so the kernel framework doesn't hang the device + * trying. The driver itself does direct calls to the PUNIT to manage + * ISP power. */ +static void isp_pm_cap_fixup(struct pci_dev *dev) +{ + dev_info(&dev->dev, "Disabling PCI power management on camera ISP\n"); + dev->pm_cap = 0; +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0f38, isp_pm_cap_fixup); |