Commit d4870725 authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Mauro Carvalho Chehab

media: staging: atomisp: Remove AP1302 sensor support

This sensor is not used by any known ACPI-enabled platform
(and no kernel users for it so far).

Just remove it for good until we get a platform which actually uses it.
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 3a81c766
...@@ -58,18 +58,6 @@ config VIDEO_ATOMISP_MT9M114 ...@@ -58,18 +58,6 @@ config VIDEO_ATOMISP_MT9M114
It currently only works with the atomisp driver. It currently only works with the atomisp driver.
config VIDEO_ATOMISP_AP1302
tristate "AP1302 external ISP support"
depends on I2C && VIDEO_V4L2
select REGMAP_I2C
---help---
This is a Video4Linux2 sensor-level driver for the external
ISP AP1302.
AP1302 is an exteral ISP.
It currently only works with the atomisp driver.
config VIDEO_ATOMISP_GC0310 config VIDEO_ATOMISP_GC0310
tristate "GC0310 sensor support" tristate "GC0310 sensor support"
depends on I2C && VIDEO_V4L2 depends on I2C && VIDEO_V4L2
......
...@@ -11,8 +11,6 @@ obj-$(CONFIG_VIDEO_ATOMISP_GC0310) += atomisp-gc0310.o ...@@ -11,8 +11,6 @@ obj-$(CONFIG_VIDEO_ATOMISP_GC0310) += atomisp-gc0310.o
obj-$(CONFIG_VIDEO_ATOMISP_MSRLIST_HELPER) += atomisp-libmsrlisthelper.o obj-$(CONFIG_VIDEO_ATOMISP_MSRLIST_HELPER) += atomisp-libmsrlisthelper.o
obj-$(CONFIG_VIDEO_ATOMISP_AP1302) += atomisp-ap1302.o
# Makefile for flash drivers # Makefile for flash drivers
# #
......
/*
*
* Copyright (c) 2013 Intel Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#ifndef __AP1302_H__
#define __AP1302_H__
#include "../include/linux/atomisp_platform.h"
#include <linux/regmap.h>
#include <linux/types.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#define AP1302_NAME "ap1302"
#define AP1302_CHIP_ID 0x265
#define AP1302_I2C_MAX_LEN 65534
#define AP1302_FW_WINDOW_OFFSET 0x8000
#define AP1302_FW_WINDOW_SIZE 0x2000
#define AP1302_REG16 2
#define AP1302_REG32 4
#define REG_CHIP_VERSION 0x0000
#define REG_CHIP_REV 0x0050
#define REG_MF_ID 0x0004
#define REG_ERROR 0x0006
#define REG_CTRL 0x1000
#define REG_DZ_TGT_FCT 0x1010
#define REG_SFX_MODE 0x1016
#define REG_SS_HEAD_PT0 0x1174
#define REG_AE_BV_OFF 0x5014
#define REG_AE_BV_BIAS 0x5016
#define REG_AWB_CTRL 0x5100
#define REG_FLICK_CTRL 0x5440
#define REG_SCENE_CTRL 0x5454
#define REG_BOOTDATA_STAGE 0x6002
#define REG_SENSOR_SELECT 0x600C
#define REG_SYS_START 0x601A
#define REG_SIP_CRC 0xF052
#define REG_PREVIEW_BASE 0x2000
#define REG_SNAPSHOT_BASE 0x3000
#define REG_VIDEO_BASE 0x4000
#define CNTX_WIDTH 0x00
#define CNTX_HEIGHT 0x02
#define CNTX_ROI_X0 0x04
#define CNTX_ROI_Y0 0x06
#define CNTX_ROI_X1 0x08
#define CNTX_ROI_Y1 0x0A
#define CNTX_ASPECT 0x0C
#define CNTX_LOCK 0x0E
#define CNTX_ENABLE 0x10
#define CNTX_OUT_FMT 0x12
#define CNTX_SENSOR_MODE 0x14
#define CNTX_MIPI_CTRL 0x16
#define CNTX_MIPI_II_CTRL 0x18
#define CNTX_LINE_TIME 0x1C
#define CNTX_MAX_FPS 0x20
#define CNTX_AE_USG 0x22
#define CNTX_AE_UPPER_ET 0x24
#define CNTX_AE_MAX_ET 0x28
#define CNTX_SS 0x2C
#define CNTX_S1_SENSOR_MODE 0x2E
#define CNTX_HINF_CTRL 0x30
#define CTRL_CNTX_MASK 0x03
#define CTRL_CNTX_OFFSET 0x00
#define HINF_CTRL_LANE_MASK 0x07
#define HINF_CTRL_LANE_OFFSET 0x00
#define MIPI_CTRL_IMGVC_MASK 0xC0
#define MIPI_CTRL_IMGVC_OFFSET 0x06
#define MIPI_CTRL_IMGTYPE_AUTO 0x3F
#define MIPI_CTRL_SSVC_MASK 0xC000
#define MIPI_CTRL_SSVC_OFFSET 0x0E
#define MIPI_CTRL_SSTYPE_MASK 0x3F00
#define MIPI_CTRL_SSTYPE_OFFSET 0x08
#define OUT_FMT_IIS_MASK 0x30
#define OUT_FMT_IIS_OFFSET 0x08
#define OUT_FMT_SS_MASK 0x1000
#define OUT_FMT_SS_OFFSET 0x12
#define OUT_FMT_TYPE_MASK 0xFF
#define SENSOR_SELECT_MASK 0x03
#define SENSOR_SELECT_OFFSET 0x00
#define AWB_CTRL_MODE_MASK 0x0F
#define AWB_CTRL_MODE_OFFSET 0x00
#define AWB_CTRL_FLASH_MASK 0x100
#define AP1302_FMT_UYVY422 0x50
#define AP1302_SYS_ACTIVATE 0x8010
#define AP1302_SYS_SWITCH 0x8140
#define AP1302_SENSOR_PRI 0x01
#define AP1302_SENSOR_SEC 0x02
#define AP1302_SS_CTRL 0x31
#define AP1302_MAX_RATIO_MISMATCH 10 /* Unit in percentage */
#define AP1302_MAX_EV 2
#define AP1302_MIN_EV -2
enum ap1302_contexts {
CONTEXT_PREVIEW = 0,
CONTEXT_SNAPSHOT,
CONTEXT_VIDEO,
CONTEXT_NUM
};
/* The context registers are defined according to preview/video registers.
Preview and video context have the same register definition.
But snapshot context does not have register S1_SENSOR_MODE.
When setting snapshot registers, if the offset exceeds
S1_SENSOR_MODE, the actual offset needs to minus 2. */
struct ap1302_context_config {
u16 width;
u16 height;
u16 roi_x0;
u16 roi_y0;
u16 roi_x1;
u16 roi_y1;
u16 aspect_factor;
u16 lock;
u16 enable;
u16 out_fmt;
u16 sensor_mode;
u16 mipi_ctrl;
u16 mipi_ii_ctrl;
u16 padding;
u32 line_time;
u16 max_fps;
u16 ae_usg;
u32 ae_upper_et;
u32 ae_max_et;
u16 ss;
u16 s1_sensor_mode;
u16 hinf_ctrl;
u32 reserved;
};
struct ap1302_res_struct {
u16 width;
u16 height;
u16 fps;
};
struct ap1302_context_res {
u32 res_num;
u32 cur_res;
struct ap1302_res_struct *res_table;
};
struct ap1302_device {
struct v4l2_subdev sd;
struct media_pad pad;
struct camera_sensor_platform_data *platform_data;
const struct firmware *fw;
struct mutex input_lock; /* serialize sensor's ioctl */
struct v4l2_mbus_framefmt format;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *run_mode;
struct ap1302_context_config cntx_config[CONTEXT_NUM];
struct ap1302_context_res cntx_res[CONTEXT_NUM];
enum ap1302_contexts cur_context;
unsigned int num_lanes;
struct regmap *regmap16;
struct regmap *regmap32;
bool sys_activated;
bool power_on;
};
struct ap1302_firmware {
u32 crc;
u32 pll_init_size;
u32 total_size;
u32 reserved;
};
struct ap1302_context_info {
u16 offset;
u16 len;
char *name;
};
#endif
/*
*
* Copyright (c) 2013 Intel Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "../include/linux/atomisp.h"
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
#include <linux/types.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include "ap1302.h"
#define to_ap1302_device(sub_dev) \
container_of(sub_dev, struct ap1302_device, sd)
/* Static definitions */
static struct regmap_config ap1302_reg16_config = {
.reg_bits = 16,
.val_bits = 16,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
static struct regmap_config ap1302_reg32_config = {
.reg_bits = 16,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
static enum ap1302_contexts ap1302_cntx_mapping[] = {
CONTEXT_PREVIEW, /* Invalid atomisp run mode */
CONTEXT_VIDEO, /* ATOMISP_RUN_MODE_VIDEO */
CONTEXT_SNAPSHOT, /* ATOMISP_RUN_MODE_STILL_CAPTURE */
CONTEXT_SNAPSHOT, /* ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE */
CONTEXT_PREVIEW, /* ATOMISP_RUN_MODE_PREVIEW */
};
static struct ap1302_res_struct ap1302_preview_res[] = {
{
.width = 640,
.height = 480,
.fps = 30,
},
{
.width = 720,
.height = 480,
.fps = 30,
},
{
.width = 1280,
.height = 720,
.fps = 30,
},
{
.width = 1920,
.height = 1080,
.fps = 30,
}
};
static struct ap1302_res_struct ap1302_snapshot_res[] = {
{
.width = 640,
.height = 480,
.fps = 30,
},
{
.width = 720,
.height = 480,
.fps = 30,
},
{
.width = 1280,
.height = 720,
.fps = 30,
},
{
.width = 1920,
.height = 1080,
.fps = 30,
}
};
static struct ap1302_res_struct ap1302_video_res[] = {
{
.width = 640,
.height = 480,
.fps = 30,
},
{
.width = 720,
.height = 480,
.fps = 30,
},
{
.width = 1280,
.height = 720,
.fps = 30,
},
{
.width = 1920,
.height = 1080,
.fps = 30,
}
};
static enum ap1302_contexts stream_to_context[] = {
CONTEXT_SNAPSHOT,
CONTEXT_PREVIEW,
CONTEXT_PREVIEW,
CONTEXT_VIDEO
};
static u16 aux_stream_config[CONTEXT_NUM][CONTEXT_NUM] = {
{0, 0, 0}, /* Preview: No aux streams. */
{1, 0, 2}, /* Snapshot: 1 for postview. 2 for video */
{1, 0, 0}, /* Video: 1 for preview. */
};
static struct ap1302_context_info context_info[] = {
{CNTX_WIDTH, AP1302_REG16, "width"},
{CNTX_HEIGHT, AP1302_REG16, "height"},
{CNTX_ROI_X0, AP1302_REG16, "roi_x0"},
{CNTX_ROI_X1, AP1302_REG16, "roi_x1"},
{CNTX_ROI_Y0, AP1302_REG16, "roi_y0"},
{CNTX_ROI_Y1, AP1302_REG16, "roi_y1"},
{CNTX_ASPECT, AP1302_REG16, "aspect"},
{CNTX_LOCK, AP1302_REG16, "lock"},
{CNTX_ENABLE, AP1302_REG16, "enable"},
{CNTX_OUT_FMT, AP1302_REG16, "out_fmt"},
{CNTX_SENSOR_MODE, AP1302_REG16, "sensor_mode"},
{CNTX_MIPI_CTRL, AP1302_REG16, "mipi_ctrl"},
{CNTX_MIPI_II_CTRL, AP1302_REG16, "mipi_ii_ctrl"},
{CNTX_LINE_TIME, AP1302_REG32, "line_time"},
{CNTX_MAX_FPS, AP1302_REG16, "max_fps"},
{CNTX_AE_USG, AP1302_REG16, "ae_usg"},
{CNTX_AE_UPPER_ET, AP1302_REG32, "ae_upper_et"},
{CNTX_AE_MAX_ET, AP1302_REG32, "ae_max_et"},
{CNTX_SS, AP1302_REG16, "ss"},
{CNTX_S1_SENSOR_MODE, AP1302_REG16, "s1_sensor_mode"},
{CNTX_HINF_CTRL, AP1302_REG16, "hinf_ctrl"},
};
/* This array stores the description list for metadata.
The metadata contains exposure settings and face
detection results. */
static u16 ap1302_ss_list[] = {
0xb01c, /* From 0x0186 with size 0x1C are exposure settings. */
0x0186,
0xb002, /* 0x71c0 is for F-number */
0x71c0,
0xb010, /* From 0x03dc with size 0x10 are face general infos. */
0x03dc,
0xb0a0, /* From 0x03e4 with size 0xa0 are face detail infos. */
0x03e4,
0xb020, /* From 0x0604 with size 0x20 are smile rate infos. */
0x0604,
0x0000
};
/* End of static definitions */
static int ap1302_i2c_read_reg(struct v4l2_subdev *sd,
u16 reg, u16 len, void *val)
{
struct ap1302_device *dev = to_ap1302_device(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
if (len == AP1302_REG16)
ret = regmap_read(dev->regmap16, reg, val);
else if (len == AP1302_REG32)
ret = regmap_read(dev->regmap32, reg, val);
else
ret = -EINVAL;
if (ret) {
dev_dbg(&client->dev, "Read reg failed. reg=0x%04X\n", reg);
return ret;
}
if (len == AP1302_REG16)
dev_dbg(&client->dev, "read_reg[0x%04X] = 0x%04X\n",
reg, *(u16 *)val);
else
dev_dbg(&client->dev, "read_reg[0x%04X] = 0x%08X\n",
reg, *(u32 *)val);
return ret;
}
static int ap1302_i2c_write_reg(struct v4l2_subdev *sd,
u16 reg, u16 len, u32 val)
{
struct ap1302_device *dev = to_ap1302_device(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
if (len == AP1302_REG16)
ret = regmap_write(dev->regmap16, reg, val);
else if (len == AP1302_REG32)
ret = regmap_write(dev->regmap32, reg, val);
else
ret = -EINVAL;
if (ret) {
dev_dbg(&client->dev, "Write reg failed. reg=0x%04X\n", reg);
return ret;
}
if (len == AP1302_REG16)
dev_dbg(&client->dev, "write_reg[0x%04X] = 0x%04X\n",
reg, (u16)val);
else
dev_dbg(&client->dev, "write_reg[0x%04X] = 0x%08X\n",
reg, (u32)val);
return ret;
}
static u16
ap1302_calculate_context_reg_addr(enum ap1302_contexts context, u16 offset)
{
u16 reg_addr;
/* The register offset is defined according to preview/video registers.
Preview and video context have the same register definition.
But snapshot context does not have register S1_SENSOR_MODE.
When setting snapshot registers, if the offset exceeds
S1_SENSOR_MODE, the actual offset needs to minus 2. */
if (context == CONTEXT_SNAPSHOT) {
if (offset == CNTX_S1_SENSOR_MODE)
return 0;
if (offset > CNTX_S1_SENSOR_MODE)
offset -= 2;
}
if (context == CONTEXT_PREVIEW)
reg_addr = REG_PREVIEW_BASE + offset;
else if (context == CONTEXT_VIDEO)
reg_addr = REG_VIDEO_BASE + offset;
else
reg_addr = REG_SNAPSHOT_BASE + offset;
return reg_addr;
}
static int ap1302_read_context_reg(struct v4l2_subdev *sd,
enum ap1302_contexts context, u16 offset, u16 len)
{
struct ap1302_device *dev = to_ap1302_device(sd);
u16 reg_addr = ap1302_calculate_context_reg_addr(context, offset);
if (reg_addr == 0)
return -EINVAL;
return ap1302_i2c_read_reg(sd, reg_addr, len,
((u8 *)&dev->cntx_config[context]) + offset);
}
static int ap1302_write_context_reg(struct v4l2_subdev *sd,
enum ap1302_contexts context, u16 offset, u16 len)
{
struct ap1302_device *dev = to_ap1302_device(sd);
u16 reg_addr = ap1302_calculate_context_reg_addr(context, offset);
if (reg_addr == 0)
return -EINVAL;
return ap1302_i2c_write_reg(sd, reg_addr, len,
*(u32 *)(((u8 *)&dev->cntx_config[context]) + offset));
}
static int ap1302_dump_context_reg(struct v4l2_subdev *sd,
enum ap1302_contexts context)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ap1302_device *dev = to_ap1302_device(sd);
int i;
dev_dbg(&client->dev, "Dump registers for context[%d]:\n", context);
for (i = 0; i < ARRAY_SIZE(context_info); i++) {
struct ap1302_context_info *info = &context_info[i];
u8 *var = (u8 *)&dev->cntx_config[context] + info->offset;
/* Snapshot context does not have s1_sensor_mode register. */
if (context == CONTEXT_SNAPSHOT &&
info->offset == CNTX_S1_SENSOR_MODE)
continue;
ap1302_read_context_reg(sd, context, info->offset, info->len);
if (info->len == AP1302_REG16)
dev_dbg(&client->dev, "context.%s = 0x%04X (%d)\n",
info->name, *(u16 *)var, *(u16 *)var);
else
dev_dbg(&client->dev, "context.%s = 0x%08X (%d)\n",
info->name, *(u32 *)var, *(u32 *)var);
}
return 0;
}
static int ap1302_request_firmware(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ap1302_device *dev = to_ap1302_device(sd);
int ret;
ret = request_firmware(&dev->fw, "ap1302_fw.bin", &client->dev);
if (ret)
dev_err(&client->dev,
"ap1302_request_firmware failed. ret=%d\n", ret);
return ret;
}
/* When loading firmware, host writes firmware data from address 0x8000.
When the address reaches 0x9FFF, the next address should return to 0x8000.
This function handles this address window and load firmware data to AP1302.
win_pos indicates the offset within this window. Firmware loading procedure
may call this function several times. win_pos records the current position
that has been written to.*/
static int ap1302_write_fw_window(struct v4l2_subdev *sd,
u16 *win_pos, const u8 *buf, u32 len)
{
struct ap1302_device *dev = to_ap1302_device(sd);
int ret;
u32 pos;
u32 sub_len;
for (pos = 0; pos < len; pos += sub_len) {
if (len - pos < AP1302_FW_WINDOW_SIZE - *win_pos)
sub_len = len - pos;
else
sub_len = AP1302_FW_WINDOW_SIZE - *win_pos;
ret = regmap_raw_write(dev->regmap16,
*win_pos + AP1302_FW_WINDOW_OFFSET,
buf + pos, sub_len);
if (ret)
return ret;
*win_pos += sub_len;
if (*win_pos >= AP1302_FW_WINDOW_SIZE)
*win_pos = 0;
}
return 0;
}
static int ap1302_load_firmware(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ap1302_device *dev = to_ap1302_device(sd);
const struct ap1302_firmware *fw;
const u8 *fw_data;
u16 reg_val = 0;
u16 win_pos = 0;
int ret;
dev_info(&client->dev, "Start to load firmware.\n");
if (!dev->fw) {
dev_err(&client->dev, "firmware not requested.\n");
return -EINVAL;
}
fw = (const struct ap1302_firmware *) dev->fw->data;
if (dev->fw->size != (sizeof(*fw) + fw->total_size)) {
dev_err(&client->dev, "firmware size does not match.\n");
return -EINVAL;
}
/* The fw binary contains a header of struct ap1302_firmware.
Following the header is the bootdata of AP1302.
The bootdata pointer can be referenced as &fw[1]. */
fw_data = (u8 *)&fw[1];
/* Clear crc register. */
ret = ap1302_i2c_write_reg(sd, REG_SIP_CRC, AP1302_REG16, 0xFFFF);
if (ret)
return ret;
/* Load FW data for PLL init stage. */
ret = ap1302_write_fw_window(sd, &win_pos, fw_data, fw->pll_init_size);
if (ret)
return ret;
/* Write 2 to bootdata_stage register to apply basic_init_hp
settings and enable PLL. */
ret = ap1302_i2c_write_reg(sd, REG_BOOTDATA_STAGE,
AP1302_REG16, 0x0002);
if (ret)
return ret;
/* Wait 1ms for PLL to lock. */
msleep(20);
/* Load the rest of bootdata content. */
ret = ap1302_write_fw_window(sd, &win_pos, fw_data + fw->pll_init_size,
fw->total_size - fw->pll_init_size);
if (ret)
return ret;
/* Check crc. */
ret = ap1302_i2c_read_reg(sd, REG_SIP_CRC, AP1302_REG16, &reg_val);
if (ret)
return ret;
if (reg_val != fw->crc) {
dev_err(&client->dev,
"crc does not match. T:0x%04X F:0x%04X\n",
fw->crc, reg_val);
return -EAGAIN;
}
/* Write 0xFFFF to bootdata_stage register to indicate AP1302 that
the whole bootdata content has been loaded. */
ret = ap1302_i2c_write_reg(sd, REG_BOOTDATA_STAGE,
AP1302_REG16, 0xFFFF);
if (ret)
return ret;
dev_info(&client->dev, "Load firmware successfully.\n");
return 0;
}
static int __ap1302_s_power(struct v4l2_subdev *sd, int on, int load_fw)
{
struct ap1302_device *dev = to_ap1302_device(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret, i;
u16 ss_ptr;
dev_info(&client->dev, "ap1302_s_power is called.\n");
ret = dev->platform_data->power_ctrl(sd, on);
if (ret) {
dev_err(&client->dev,
"ap1302_s_power error. on=%d ret=%d\n", on, ret);
return ret;
}
dev->power_on = on;
if (!on || !load_fw)
return 0;
/* Load firmware after power on. */
ret = ap1302_load_firmware(sd);
if (ret) {
dev_err(&client->dev,
"ap1302_load_firmware failed. ret=%d\n", ret);
return ret;
}
ret = ap1302_i2c_read_reg(sd, REG_SS_HEAD_PT0, AP1302_REG16, &ss_ptr);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(ap1302_ss_list); i++) {
ret = ap1302_i2c_write_reg(sd, ss_ptr + i * 2,
AP1302_REG16, ap1302_ss_list[i]);
if (ret)
return ret;
}
return ret;
}
static int ap1302_s_power(struct v4l2_subdev *sd, int on)
{
struct ap1302_device *dev = to_ap1302_device(sd);
int ret;
mutex_lock(&dev->input_lock);
ret = __ap1302_s_power(sd, on, 1);
dev->sys_activated = 0;
mutex_unlock(&dev->input_lock);
return ret;
}
static int ap1302_s_config(struct v4l2_subdev *sd, void *pdata)
{
struct ap1302_device *dev = to_ap1302_device(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct camera_mipi_info *mipi_info;
u16 reg_val = 0;
int ret;
dev_info(&client->dev, "ap1302_s_config is called.\n");
if (pdata == NULL)
return -ENODEV;
dev->platform_data = pdata;
mutex_lock(&dev->input_lock);
if (dev->platform_data->platform_init) {
ret = dev->platform_data->platform_init(client);
if (ret)
goto fail_power;
}
ret = __ap1302_s_power(sd, 1, 0);
if (ret)
goto fail_power;
/* Detect for AP1302 */
ret = ap1302_i2c_read_reg(sd, REG_CHIP_VERSION, AP1302_REG16, &reg_val);
if (ret || (reg_val != AP1302_CHIP_ID)) {
dev_err(&client->dev,
"Chip version does no match. ret=%d ver=0x%04x\n",
ret, reg_val);
goto fail_config;
}
dev_info(&client->dev, "AP1302 Chip ID is 0x%X\n", reg_val);
/* Detect revision for AP1302 */
ret = ap1302_i2c_read_reg(sd, REG_CHIP_REV, AP1302_REG16, &reg_val);
if (ret)
goto fail_config;
dev_info(&client->dev, "AP1302 Chip Rev is 0x%X\n", reg_val);
ret = dev->platform_data->csi_cfg(sd, 1);
if (ret)
goto fail_config;
mipi_info = v4l2_get_subdev_hostdata(sd);
if (!mipi_info)
goto fail_config;
dev->num_lanes = mipi_info->num_lanes;
ret = __ap1302_s_power(sd, 0, 0);
if (ret)
goto fail_power;
mutex_unlock(&dev->input_lock);
return ret;
fail_config:
__ap1302_s_power(sd, 0, 0);
fail_power:
mutex_unlock(&dev->input_lock);
dev_err(&client->dev, "ap1302_s_config failed\n");
return ret;
}
static enum ap1302_contexts ap1302_get_context(struct v4l2_subdev *sd)
{
struct ap1302_device *dev = to_ap1302_device(sd);
return dev->cur_context;
}
static int ap1302_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
if (code->index)
return -EINVAL;
code->code = MEDIA_BUS_FMT_UYVY8_1X16;
return 0;
}
static int ap1302_match_resolution(struct ap1302_context_res *res,
struct v4l2_mbus_framefmt *fmt)
{
s32 w0, h0, mismatch, distance;
s32 w1 = fmt->width;
s32 h1 = fmt->height;
s32 min_distance = INT_MAX;
s32 i, idx = -1;
if (w1 == 0 || h1 == 0)
return -1;
for (i = 0; i < res->res_num; i++) {
w0 = res->res_table[i].width;
h0 = res->res_table[i].height;
if (w0 < w1 || h0 < h1)
continue;
mismatch = abs(w0 * h1 - w1 * h0) * 8192 / w1 / h0;
if (mismatch > 8192 * AP1302_MAX_RATIO_MISMATCH / 100)
continue;
distance = (w0 * h1 + w1 * h0) * 8192 / w1 / h1;
if (distance < min_distance) {
min_distance = distance;
idx = i;
}
}
return idx;
}
static s32 ap1302_try_mbus_fmt_locked(struct v4l2_subdev *sd,
enum ap1302_contexts context,
struct v4l2_mbus_framefmt *fmt)
{
struct ap1302_device *dev = to_ap1302_device(sd);
struct ap1302_res_struct *res_table;
s32 res_num, idx = -1;
res_table = dev->cntx_res[context].res_table;
res_num = dev->cntx_res[context].res_num;
if ((fmt->width <= res_table[res_num - 1].width) &&
(fmt->height <= res_table[res_num - 1].height))
idx = ap1302_match_resolution(&dev->cntx_res[context], fmt);
if (idx == -1)
idx = res_num - 1;
fmt->width = res_table[idx].width;
fmt->height = res_table[idx].height;
fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
return idx;
}
static int ap1302_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt = &format->format;
struct ap1302_device *dev = to_ap1302_device(sd);
enum ap1302_contexts context;
struct ap1302_res_struct *res_table;
s32 cur_res;
if (format->pad)
return -EINVAL;
mutex_lock(&dev->input_lock);
context = ap1302_get_context(sd);
res_table = dev->cntx_res[context].res_table;
cur_res = dev->cntx_res[context].cur_res;
fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
fmt->width = res_table[cur_res].width;
fmt->height = res_table[cur_res].height;
mutex_unlock(&dev->input_lock);
return 0;
}
static int ap1302_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt = &format->format;
struct ap1302_device *dev = to_ap1302_device(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct atomisp_input_stream_info *stream_info =
(struct atomisp_input_stream_info *)fmt->reserved;
enum ap1302_contexts context, main_context;
if (format->pad)
return -EINVAL;
if (!fmt)
return -EINVAL;
mutex_lock(&dev->input_lock);
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
context = ap1302_get_context(sd);
ap1302_try_mbus_fmt_locked(sd, context, fmt);
cfg->try_fmt = *fmt;
mutex_unlock(&dev->input_lock);
return 0;
}
context = stream_to_context[stream_info->stream];
dev_dbg(&client->dev, "ap1302_set_mbus_fmt. stream=%d context=%d\n",
stream_info->stream, context);
dev->cntx_res[context].cur_res =
ap1302_try_mbus_fmt_locked(sd, context, fmt);
dev->cntx_config[context].width = fmt->width;
dev->cntx_config[context].height = fmt->height;
ap1302_write_context_reg(sd, context, CNTX_WIDTH, AP1302_REG16);
ap1302_write_context_reg(sd, context, CNTX_HEIGHT, AP1302_REG16);
ap1302_read_context_reg(sd, context, CNTX_OUT_FMT, AP1302_REG16);
dev->cntx_config[context].out_fmt &= ~OUT_FMT_TYPE_MASK;
dev->cntx_config[context].out_fmt |= AP1302_FMT_UYVY422;
ap1302_write_context_reg(sd, context, CNTX_OUT_FMT, AP1302_REG16);
main_context = ap1302_get_context(sd);
if (context == main_context) {
ap1302_read_context_reg(sd, context,
CNTX_MIPI_CTRL, AP1302_REG16);
dev->cntx_config[context].mipi_ctrl &= ~MIPI_CTRL_IMGVC_MASK;
dev->cntx_config[context].mipi_ctrl |=
(context << MIPI_CTRL_IMGVC_OFFSET);
dev->cntx_config[context].mipi_ctrl &= ~MIPI_CTRL_SSVC_MASK;
dev->cntx_config[context].mipi_ctrl |=
(context << MIPI_CTRL_SSVC_OFFSET);
dev->cntx_config[context].mipi_ctrl &= ~MIPI_CTRL_SSTYPE_MASK;
dev->cntx_config[context].mipi_ctrl |=
(0x12 << MIPI_CTRL_SSTYPE_OFFSET);
ap1302_write_context_reg(sd, context,
CNTX_MIPI_CTRL, AP1302_REG16);
ap1302_read_context_reg(sd, context,
CNTX_SS, AP1302_REG16);
dev->cntx_config[context].ss = AP1302_SS_CTRL;
ap1302_write_context_reg(sd, context,
CNTX_SS, AP1302_REG16);
} else {
/* Configure aux stream */
ap1302_read_context_reg(sd, context,
CNTX_MIPI_II_CTRL, AP1302_REG16);
dev->cntx_config[context].mipi_ii_ctrl &= ~MIPI_CTRL_IMGVC_MASK;
dev->cntx_config[context].mipi_ii_ctrl |=
(context << MIPI_CTRL_IMGVC_OFFSET);
ap1302_write_context_reg(sd, context,
CNTX_MIPI_II_CTRL, AP1302_REG16);
if (stream_info->enable) {
ap1302_read_context_reg(sd, main_context,
CNTX_OUT_FMT, AP1302_REG16);
dev->cntx_config[context].out_fmt |=
(aux_stream_config[main_context][context]
<< OUT_FMT_IIS_OFFSET);
ap1302_write_context_reg(sd, main_context,
CNTX_OUT_FMT, AP1302_REG16);
}
}
stream_info->ch_id = context;
mutex_unlock(&dev->input_lock);
return 0;
}
static int ap1302_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
struct ap1302_device *dev = to_ap1302_device(sd);
enum ap1302_contexts context;
struct ap1302_res_struct *res_table;
u32 cur_res;
mutex_lock(&dev->input_lock);
context = ap1302_get_context(sd);
res_table = dev->cntx_res[context].res_table;
cur_res = dev->cntx_res[context].cur_res;
interval->interval.denominator = res_table[cur_res].fps;
interval->interval.numerator = 1;
mutex_unlock(&dev->input_lock);
return 0;
}
static int ap1302_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct ap1302_device *dev = to_ap1302_device(sd);
enum ap1302_contexts context;
struct ap1302_res_struct *res_table;
int index = fse->index;
mutex_lock(&dev->input_lock);
context = ap1302_get_context(sd);
if (index >= dev->cntx_res[context].res_num) {
mutex_unlock(&dev->input_lock);
return -EINVAL;
}
res_table = dev->cntx_res[context].res_table;
fse->min_width = res_table[index].width;
fse->min_height = res_table[index].height;
fse->max_width = res_table[index].width;
fse->max_height = res_table[index].height;
mutex_unlock(&dev->input_lock);
return 0;
}
static int ap1302_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
{
*frames = 0;
return 0;
}
static int ap1302_s_stream(struct v4l2_subdev *sd, int enable)
{
struct ap1302_device *dev = to_ap1302_device(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
enum ap1302_contexts context;
u32 reg_val;
int ret;
mutex_lock(&dev->input_lock);
context = ap1302_get_context(sd);
dev_dbg(&client->dev, "ap1302_s_stream. context=%d enable=%d\n",
context, enable);
/* Switch context */
ap1302_i2c_read_reg(sd, REG_CTRL,
AP1302_REG16, &reg_val);
reg_val &= ~CTRL_CNTX_MASK;
reg_val |= (context<<CTRL_CNTX_OFFSET);
ap1302_i2c_write_reg(sd, REG_CTRL,
AP1302_REG16, reg_val);
/* Select sensor */
ap1302_i2c_read_reg(sd, REG_SENSOR_SELECT,
AP1302_REG16, &reg_val);
reg_val &= ~SENSOR_SELECT_MASK;
reg_val |= (AP1302_SENSOR_PRI<<SENSOR_SELECT_OFFSET);
ap1302_i2c_write_reg(sd, REG_SENSOR_SELECT,
AP1302_REG16, reg_val);
if (enable) {
dev_info(&client->dev, "Start stream. context=%d\n", context);
ap1302_dump_context_reg(sd, context);
if (!dev->sys_activated) {
reg_val = AP1302_SYS_ACTIVATE;
dev->sys_activated = 1;
} else {
reg_val = AP1302_SYS_SWITCH;
}
} else {
dev_info(&client->dev, "Stop stream. context=%d\n", context);
reg_val = AP1302_SYS_SWITCH;
}
ret = ap1302_i2c_write_reg(sd, REG_SYS_START, AP1302_REG16, reg_val);
if (ret)
dev_err(&client->dev,
"AP1302 set stream failed. enable=%d\n", enable);
mutex_unlock(&dev->input_lock);
return ret;
}
static u16 ap1302_ev_values[] = {0xfd00, 0xfe80, 0x0, 0x180, 0x300};
static int ap1302_set_exposure_off(struct v4l2_subdev *sd, s32 val)
{
val -= AP1302_MIN_EV;
return ap1302_i2c_write_reg(sd, REG_AE_BV_OFF, AP1302_REG16,
ap1302_ev_values[val]);
}
static u16 ap1302_wb_values[] = {
0, /* V4L2_WHITE_BALANCE_MANUAL */
0xf, /* V4L2_WHITE_BALANCE_AUTO */
0x2, /* V4L2_WHITE_BALANCE_INCANDESCENT */
0x4, /* V4L2_WHITE_BALANCE_FLUORESCENT */
0x5, /* V4L2_WHITE_BALANCE_FLUORESCENT_H */
0x1, /* V4L2_WHITE_BALANCE_HORIZON */
0x5, /* V4L2_WHITE_BALANCE_DAYLIGHT */
0xf, /* V4L2_WHITE_BALANCE_FLASH */
0x6, /* V4L2_WHITE_BALANCE_CLOUDY */
0x6, /* V4L2_WHITE_BALANCE_SHADE */
};
static int ap1302_set_wb_mode(struct v4l2_subdev *sd, s32 val)
{
int ret = 0;
u16 reg_val;
ret = ap1302_i2c_read_reg(sd, REG_AWB_CTRL, AP1302_REG16, &reg_val);
if (ret)
return ret;
reg_val &= ~AWB_CTRL_MODE_MASK;
reg_val |= ap1302_wb_values[val] << AWB_CTRL_MODE_OFFSET;
if (val == V4L2_WHITE_BALANCE_FLASH)
reg_val |= AWB_CTRL_FLASH_MASK;
else
reg_val &= ~AWB_CTRL_FLASH_MASK;
ret = ap1302_i2c_write_reg(sd, REG_AWB_CTRL, AP1302_REG16, reg_val);
return ret;
}
static int ap1302_set_zoom(struct v4l2_subdev *sd, s32 val)
{
ap1302_i2c_write_reg(sd, REG_DZ_TGT_FCT, AP1302_REG16,
val * 4 + 0x100);
return 0;
}
static u16 ap1302_sfx_values[] = {
0x00, /* V4L2_COLORFX_NONE */
0x03, /* V4L2_COLORFX_BW */
0x0d, /* V4L2_COLORFX_SEPIA */
0x07, /* V4L2_COLORFX_NEGATIVE */
0x04, /* V4L2_COLORFX_EMBOSS */
0x0f, /* V4L2_COLORFX_SKETCH */
0x08, /* V4L2_COLORFX_SKY_BLUE */
0x09, /* V4L2_COLORFX_GRASS_GREEN */
0x0a, /* V4L2_COLORFX_SKIN_WHITEN */
0x00, /* V4L2_COLORFX_VIVID */
0x00, /* V4L2_COLORFX_AQUA */
0x00, /* V4L2_COLORFX_ART_FREEZE */
0x00, /* V4L2_COLORFX_SILHOUETTE */
0x10, /* V4L2_COLORFX_SOLARIZATION */
0x02, /* V4L2_COLORFX_ANTIQUE */
0x00, /* V4L2_COLORFX_SET_CBCR */
};
static int ap1302_set_special_effect(struct v4l2_subdev *sd, s32 val)
{
ap1302_i2c_write_reg(sd, REG_SFX_MODE, AP1302_REG16,
ap1302_sfx_values[val]);
return 0;
}
static u16 ap1302_scene_mode_values[] = {
0x00, /* V4L2_SCENE_MODE_NONE */
0x07, /* V4L2_SCENE_MODE_BACKLIGHT */
0x0a, /* V4L2_SCENE_MODE_BEACH_SNOW */
0x06, /* V4L2_SCENE_MODE_CANDLE_LIGHT */
0x00, /* V4L2_SCENE_MODE_DAWN_DUSK */
0x00, /* V4L2_SCENE_MODE_FALL_COLORS */
0x0d, /* V4L2_SCENE_MODE_FIREWORKS */
0x02, /* V4L2_SCENE_MODE_LANDSCAPE */
0x05, /* V4L2_SCENE_MODE_NIGHT */
0x0c, /* V4L2_SCENE_MODE_PARTY_INDOOR */
0x01, /* V4L2_SCENE_MODE_PORTRAIT */
0x03, /* V4L2_SCENE_MODE_SPORTS */
0x0e, /* V4L2_SCENE_MODE_SUNSET */
0x0b, /* V4L2_SCENE_MODE_TEXT */
};
static int ap1302_set_scene_mode(struct v4l2_subdev *sd, s32 val)
{
ap1302_i2c_write_reg(sd, REG_SCENE_CTRL, AP1302_REG16,
ap1302_scene_mode_values[val]);
return 0;
}
static u16 ap1302_flicker_values[] = {
0x0, /* OFF */
0x3201, /* 50HZ */
0x3c01, /* 60HZ */
0x2 /* AUTO */
};
static int ap1302_set_flicker_freq(struct v4l2_subdev *sd, s32 val)
{
ap1302_i2c_write_reg(sd, REG_FLICK_CTRL, AP1302_REG16,
ap1302_flicker_values[val]);
return 0;
}
static int ap1302_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct ap1302_device *dev = container_of(
ctrl->handler, struct ap1302_device, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_RUN_MODE:
dev->cur_context = ap1302_cntx_mapping[ctrl->val];
break;
case V4L2_CID_EXPOSURE:
ap1302_set_exposure_off(&dev->sd, ctrl->val);
break;
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
ap1302_set_wb_mode(&dev->sd, ctrl->val);
break;
case V4L2_CID_ZOOM_ABSOLUTE:
ap1302_set_zoom(&dev->sd, ctrl->val);
break;
case V4L2_CID_COLORFX:
ap1302_set_special_effect(&dev->sd, ctrl->val);
break;
case V4L2_CID_SCENE_MODE:
ap1302_set_scene_mode(&dev->sd, ctrl->val);
break;
case V4L2_CID_POWER_LINE_FREQUENCY:
ap1302_set_flicker_freq(&dev->sd, ctrl->val);
break;
default:
return -EINVAL;
}
return 0;
}
static int ap1302_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct ap1302_device *dev = to_ap1302_device(sd);
int ret;
u32 reg_val;
if (reg->size != AP1302_REG16 &&
reg->size != AP1302_REG32)
return -EINVAL;
mutex_lock(&dev->input_lock);
if (dev->power_on)
ret = ap1302_i2c_read_reg(sd, reg->reg, reg->size, &reg_val);
else
ret = -EIO;
mutex_unlock(&dev->input_lock);
if (ret)
return ret;
reg->val = reg_val;
return 0;
}
static int ap1302_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct ap1302_device *dev = to_ap1302_device(sd);
int ret;
if (reg->size != AP1302_REG16 &&
reg->size != AP1302_REG32)
return -EINVAL;
mutex_lock(&dev->input_lock);
if (dev->power_on)
ret = ap1302_i2c_write_reg(sd, reg->reg, reg->size, reg->val);
else
ret = -EIO;
mutex_unlock(&dev->input_lock);
return ret;
}
static long ap1302_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
long ret = 0;
switch (cmd) {
case VIDIOC_DBG_G_REGISTER:
ret = ap1302_g_register(sd, arg);
break;
case VIDIOC_DBG_S_REGISTER:
ret = ap1302_s_register(sd, arg);
break;
default:
ret = -EINVAL;
}
return ret;
}
static const struct v4l2_ctrl_ops ctrl_ops = {
.s_ctrl = ap1302_s_ctrl,
};
static const char * const ctrl_run_mode_menu[] = {
NULL,
"Video",
"Still capture",
"Continuous capture",
"Preview",
};
static const struct v4l2_ctrl_config ctrls[] = {
{
.ops = &ctrl_ops,
.id = V4L2_CID_RUN_MODE,
.name = "Run Mode",
.type = V4L2_CTRL_TYPE_MENU,
.min = 1,
.def = 4,
.max = 4,
.qmenu = ctrl_run_mode_menu,
},
{
.ops = &ctrl_ops,
.id = V4L2_CID_EXPOSURE,
.name = "Exposure",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = AP1302_MIN_EV,
.def = 0,
.max = AP1302_MAX_EV,
.step = 1,
},
{
.ops = &ctrl_ops,
.id = V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
.name = "White Balance",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.def = 0,
.max = 9,
.step = 1,
},
{
.ops = &ctrl_ops,
.id = V4L2_CID_ZOOM_ABSOLUTE,
.name = "Zoom Absolute",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.def = 0,
.max = 1024,
.step = 1,
},
{
.ops = &ctrl_ops,
.id = V4L2_CID_COLORFX,
.name = "Color Special Effect",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.def = 0,
.max = 15,
.step = 1,
},
{
.ops = &ctrl_ops,
.id = V4L2_CID_SCENE_MODE,
.name = "Scene Mode",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.def = 0,
.max = 13,
.step = 1,
},
{
.ops = &ctrl_ops,
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.name = "Light frequency filter",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.def = 3,
.max = 3,
.step = 1,
},
};
static const struct v4l2_subdev_sensor_ops ap1302_sensor_ops = {
.g_skip_frames = ap1302_g_skip_frames,
};
static const struct v4l2_subdev_video_ops ap1302_video_ops = {
.s_stream = ap1302_s_stream,
.g_frame_interval = ap1302_g_frame_interval,
};
static const struct v4l2_subdev_core_ops ap1302_core_ops = {
.s_power = ap1302_s_power,
.ioctl = ap1302_ioctl,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ap1302_g_register,
.s_register = ap1302_s_register,
#endif
};
static const struct v4l2_subdev_pad_ops ap1302_pad_ops = {
.enum_mbus_code = ap1302_enum_mbus_code,
.enum_frame_size = ap1302_enum_frame_size,
.get_fmt = ap1302_get_fmt,
.set_fmt = ap1302_set_fmt,
};
static const struct v4l2_subdev_ops ap1302_ops = {
.core = &ap1302_core_ops,
.pad = &ap1302_pad_ops,
.video = &ap1302_video_ops,
.sensor = &ap1302_sensor_ops
};
static int ap1302_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ap1302_device *dev = to_ap1302_device(sd);
if (dev->platform_data->platform_deinit)
dev->platform_data->platform_deinit();
release_firmware(dev->fw);
media_entity_cleanup(&dev->sd.entity);
dev->platform_data->csi_cfg(sd, 0);
v4l2_device_unregister_subdev(sd);
return 0;
}
static int ap1302_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ap1302_device *dev;
int ret;
unsigned int i;
dev_info(&client->dev, "ap1302 probe called.\n");
/* allocate device & init sub device */
dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
mutex_init(&dev->input_lock);
v4l2_i2c_subdev_init(&(dev->sd), client, &ap1302_ops);
ret = ap1302_request_firmware(&(dev->sd));
if (ret) {
dev_err(&client->dev, "Cannot request ap1302 firmware.\n");
goto out_free;
}
dev->regmap16 = devm_regmap_init_i2c(client, &ap1302_reg16_config);
if (IS_ERR(dev->regmap16)) {
ret = PTR_ERR(dev->regmap16);
dev_err(&client->dev,
"Failed to allocate 16bit register map: %d\n", ret);
return ret;
}
dev->regmap32 = devm_regmap_init_i2c(client, &ap1302_reg32_config);
if (IS_ERR(dev->regmap32)) {
ret = PTR_ERR(dev->regmap32);
dev_err(&client->dev,
"Failed to allocate 32bit register map: %d\n", ret);
return ret;
}
if (client->dev.platform_data) {
ret = ap1302_s_config(&dev->sd, client->dev.platform_data);
if (ret)
goto out_free;
}
dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
dev->pad.flags = MEDIA_PAD_FL_SOURCE;
dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
dev->cntx_res[CONTEXT_PREVIEW].res_num = ARRAY_SIZE(ap1302_preview_res);
dev->cntx_res[CONTEXT_PREVIEW].res_table = ap1302_preview_res;
dev->cntx_res[CONTEXT_SNAPSHOT].res_num =
ARRAY_SIZE(ap1302_snapshot_res);
dev->cntx_res[CONTEXT_SNAPSHOT].res_table = ap1302_snapshot_res;
dev->cntx_res[CONTEXT_VIDEO].res_num = ARRAY_SIZE(ap1302_video_res);
dev->cntx_res[CONTEXT_VIDEO].res_table = ap1302_video_res;
ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls));
if (ret) {
ap1302_remove(client);
return ret;
}
for (i = 0; i < ARRAY_SIZE(ctrls); i++)
v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL);
if (dev->ctrl_handler.error) {
ap1302_remove(client);
return dev->ctrl_handler.error;
}
/* Use same lock for controls as for everything else. */
dev->ctrl_handler.lock = &dev->input_lock;
dev->sd.ctrl_handler = &dev->ctrl_handler;
v4l2_ctrl_handler_setup(&dev->ctrl_handler);
dev->run_mode = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_RUN_MODE);
v4l2_ctrl_s_ctrl(dev->run_mode, ATOMISP_RUN_MODE_PREVIEW);
ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
if (ret)
ap1302_remove(client);
return ret;
out_free:
v4l2_device_unregister_subdev(&dev->sd);
return ret;
}
static const struct i2c_device_id ap1302_id[] = {
{AP1302_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ap1302_id);
static struct i2c_driver ap1302_driver = {
.driver = {
.name = AP1302_NAME,
},
.probe = ap1302_probe,
.remove = ap1302_remove,
.id_table = ap1302_id,
};
module_i2c_driver(ap1302_driver);
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
MODULE_DESCRIPTION("AP1302 Driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment