blob: edaae93af8f90a7b80bf71717d6b45ff0cac4b0e [file] [log] [blame]
#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 &caps;
}
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;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
int i, j, ret;
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;
ev->var.DataSize = *out_len;
ret = efivar_entry_get(ev, &ev->var.Attributes,
&ev->var.DataSize, ev->var.Data);
if (ret == 0) {
memcpy(out, ev->var.Data, ev->var.DataSize);
*out_len = ev->var.DataSize;
} else if (dev) {
dev_warn(dev, "Failed to find gmin variable %s\n", var8);
}
kfree(ev);
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);