1245 lines
31 KiB
C
1245 lines
31 KiB
C
/*
|
|
* Support for HM2056_raw Camera Sensor.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/gpio.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-chip-ident.h>
|
|
|
|
#include "hm2056_raw.h"
|
|
|
|
#define to_hm2056_raw_sensor(sd) container_of(sd, struct hm2056_raw_device, sd)
|
|
|
|
//Add for ATD command+++
|
|
extern int build_version; //Add build version -> user:3, userdebug:2, eng:1
|
|
struct v4l2_subdev *main_sd;
|
|
|
|
int ATD_hm2056_raw_status = 0;
|
|
static char camera_module_otp[60];
|
|
|
|
//static void *hm2056_raw_otp_read(struct v4l2_subdev *sd);
|
|
|
|
static ssize_t hm2056_raw_show_status(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
printk("%s: get hm2056_raw status (%d) !!\n", __func__, ATD_hm2056_raw_status);
|
|
//Check sensor connect status, just do it in begining for ATD camera status
|
|
|
|
return sprintf(buf, "%d\n", ATD_hm2056_raw_status);
|
|
}
|
|
|
|
static ssize_t hm2056_raw_read_otp(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
printk("%s: get hm2056 module OTP %s !!\n", __func__, camera_module_otp);
|
|
//Check sensor OTP value, just do it in begining for ATD camera status
|
|
/*
|
|
if(build_version != 1){ //not eng, need to read otp first
|
|
hm2056_raw_otp_read(main_sd);
|
|
}
|
|
*/
|
|
return sprintf(buf, "%s", camera_module_otp);
|
|
}
|
|
|
|
static DEVICE_ATTR(hm2056_raw_status, S_IRUGO, hm2056_raw_show_status, NULL);
|
|
static DEVICE_ATTR(hm2056_raw_read_otp, S_IRUGO, hm2056_raw_read_otp, NULL);
|
|
|
|
static struct attribute *hm2056_raw_attributes[] = {
|
|
&dev_attr_hm2056_raw_status.attr,
|
|
&dev_attr_hm2056_raw_read_otp.attr,
|
|
NULL
|
|
};
|
|
//Add for ATD command---
|
|
|
|
static int
|
|
hm2056_raw_read_reg(struct i2c_client *client, u16 data_length, u32 reg, u32 *val)
|
|
{
|
|
int err;
|
|
struct i2c_msg msg[2];
|
|
unsigned char data[2];
|
|
|
|
if (!client->adapter) {
|
|
v4l2_err(client, "%s error, no client->adapter\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (data_length != HM2056_8BIT) {
|
|
v4l2_err(client, "%s error, invalid data length\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = MSG_LEN_OFFSET;
|
|
msg[0].buf = data;
|
|
|
|
/* high byte goes out first */
|
|
data[0] = (u16) (reg >> 8);
|
|
data[1] = (u16) (reg & 0xff);
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].len = data_length;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].buf = data;
|
|
|
|
err = i2c_transfer(client->adapter, msg, 2);
|
|
if (err >= 0) {
|
|
*val = data[0];
|
|
return 0;
|
|
}
|
|
|
|
dev_err(&client->dev, "read from offset 0x%x error %d", reg, err);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
hm2056_raw_write_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 val)
|
|
{
|
|
int num_msg;
|
|
struct i2c_msg msg;
|
|
unsigned char data[6] = {0};
|
|
u16 *wreg;
|
|
int retry = 0;
|
|
|
|
if (!client->adapter) {
|
|
v4l2_err(client, "%s error, no client->adapter\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (data_length != HM2056_8BIT) {
|
|
v4l2_err(client, "%s error, invalid data_length\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
again:
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = sizeof(u16) + data_length;
|
|
msg.buf = data;
|
|
|
|
/* high byte goes out first */
|
|
wreg = (u16 *)data;
|
|
*wreg = cpu_to_be16(reg);
|
|
|
|
data[2] = (u8)(val);
|
|
|
|
num_msg = i2c_transfer(client->adapter, &msg, 1);
|
|
if (num_msg >= 0)
|
|
return 0;
|
|
|
|
dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d",
|
|
val, reg, num_msg);
|
|
if (retry <= I2C_RETRY_COUNT) {
|
|
dev_dbg(&client->dev, "retrying... %d", retry);
|
|
retry++;
|
|
msleep(20);
|
|
goto again;
|
|
}
|
|
|
|
return num_msg;
|
|
}
|
|
|
|
/*
|
|
* hm2056_raw_write_reg_array - write a list of hm2056_raw registers
|
|
* @client: i2c driver client structure
|
|
* @reglist: list of registers to be written
|
|
*
|
|
* This function write a list of registers.
|
|
*/
|
|
static int hm2056_raw_write_reg_array(struct i2c_client *client,
|
|
const struct hm2056_reg *reglist)
|
|
{
|
|
const struct hm2056_reg *next = reglist;
|
|
struct hm2056_write_ctrl ctrl;
|
|
int ret;
|
|
|
|
ctrl.index = 0;
|
|
for (; next->type != HM2056_TOK_TERM; next++) {
|
|
switch (next->type & HM2056_TOK_MASK) {
|
|
case HM2056_TOK_DELAY:
|
|
msleep(next->val);
|
|
break;
|
|
default:
|
|
ret = hm2056_raw_write_reg(client, next->type, next->reg, next->val);
|
|
if (ret) {
|
|
v4l2_err(client, "%s: write %x error, aborted\n",
|
|
__func__, next->reg);
|
|
return ret;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_set_suspend(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
|
|
ret = hm2056_raw_write_reg_array(client, hm2056_stream_off);
|
|
if (ret)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_set_streaming(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
|
|
ret = hm2056_raw_write_reg_array(client, hm2056_stream_on);
|
|
if (ret)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int power_up(struct v4l2_subdev *sd)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
|
|
if (NULL == dev->platform_data) {
|
|
dev_err(&client->dev, "no camera_sensor_platform_data");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* power control */
|
|
ret = dev->platform_data->power_ctrl(sd, 1);
|
|
if (ret) {
|
|
dev_err(&client->dev, "power_ctrl failed\n");
|
|
goto fail_power;
|
|
}
|
|
|
|
/* flis clock control */
|
|
ret = dev->platform_data->flisclk_ctrl(sd, 1);
|
|
if (ret) {
|
|
dev_err(&client->dev, "flisclk_ctrl failed\n");
|
|
goto fail_clk;
|
|
}
|
|
msleep(10);
|
|
/* gpio ctrl */
|
|
ret = dev->platform_data->gpio_ctrl(sd, 1);
|
|
if (ret) {
|
|
dev_err(&client->dev, "gpio_ctrl failed\n");
|
|
}
|
|
|
|
msleep(50);
|
|
|
|
return 0;
|
|
|
|
fail_clk:
|
|
dev->platform_data->flisclk_ctrl(sd, 0);
|
|
fail_power:
|
|
dev->platform_data->power_ctrl(sd, 0);
|
|
dev_err(&client->dev, "sensor power-up failed\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int power_down(struct v4l2_subdev *sd)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
|
|
if (NULL == dev->platform_data) {
|
|
dev_err(&client->dev, "no camera_sensor_platform_data\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = dev->platform_data->flisclk_ctrl(sd, 0);
|
|
if (ret)
|
|
dev_err(&client->dev, "flisclk failed\n");
|
|
|
|
/* gpio ctrl */
|
|
ret = dev->platform_data->gpio_ctrl(sd, 0);
|
|
if (ret)
|
|
dev_err(&client->dev, "gpio failed\n");
|
|
|
|
/* power control */
|
|
ret = dev->platform_data->power_ctrl(sd, 0);
|
|
if (ret)
|
|
dev_err(&client->dev, "vprog failed.\n");
|
|
|
|
/*according to DS, 20ms is needed after power down*/
|
|
msleep(20);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hm2056_raw_s_power(struct v4l2_subdev *sd, int power)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
int ret;
|
|
|
|
dev_info(&client->dev, "%s on/off %d\n", __func__, power);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (power == 0) {
|
|
ret = power_down(sd);
|
|
} else {
|
|
ret = power_up(sd);
|
|
if (!ret) {
|
|
ret = hm2056_raw_write_reg_array(client, hm2056_init);
|
|
}
|
|
}
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int hm2056_raw_to_res(struct v4l2_subdev *sd, u32 w, u32 h)
|
|
{
|
|
int idx;
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
for (idx = 0; idx < dev->n_res; idx++) {
|
|
if ((dev->hm2056_raw_res[idx].width == w) &&
|
|
(dev->hm2056_raw_res[idx].height == h))
|
|
break;
|
|
}
|
|
|
|
/* No mode found */
|
|
if (idx >= dev->n_res)
|
|
return -1;
|
|
return idx;
|
|
|
|
|
|
}
|
|
static int hm2056_raw_try_res(struct v4l2_subdev *sd, u32 *w, u32 *h)
|
|
{
|
|
int idx;
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
/*
|
|
* The mode list is in ascending order. We're done as soon as
|
|
* we have found the first equal or bigger size.
|
|
*/
|
|
for (idx = 0; idx < dev->n_res; idx++) {
|
|
if ((dev->hm2056_raw_res[idx].width >= *w) &&
|
|
(dev->hm2056_raw_res[idx].height >= *h))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If no mode was found, it means we can provide only a smaller size.
|
|
* Returning the biggest one available in this case.
|
|
*/
|
|
if (idx == dev->n_res)
|
|
idx--;
|
|
|
|
*w = dev->hm2056_raw_res[idx].width;
|
|
*h = dev->hm2056_raw_res[idx].height;
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_try_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
int ret;
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
ret = hm2056_raw_try_res(sd, &fmt->width, &fmt->height);
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int hm2056_raw_get_intg_factor(struct i2c_client *client,
|
|
struct camera_mipi_info *info,
|
|
const struct hm2056_raw_res_struct *res)
|
|
{
|
|
struct atomisp_sensor_mode_data *buf = &info->data;
|
|
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
if (info == NULL || res == NULL)
|
|
return -EINVAL;
|
|
|
|
buf->vt_pix_clk_freq_mhz = res->pixel_clk;
|
|
buf->frame_length_lines = res->frame_length_lines;
|
|
buf->line_length_pck = res->line_length_pck;
|
|
|
|
/* get integration time */
|
|
buf->coarse_integration_time_min = HM2056_COARSE_INTG_TIME_MIN;
|
|
buf->coarse_integration_time_max_margin =
|
|
HM2056_COARSE_INTG_TIME_MAX_MARGIN;
|
|
|
|
buf->fine_integration_time_min = HM2056_FINE_INTG_TIME_MIN;
|
|
buf->fine_integration_time_max_margin =
|
|
HM2056_FINE_INTG_TIME_MAX_MARGIN;
|
|
|
|
buf->fine_integration_time_def = HM2056_FINE_INTG_TIME_MIN;
|
|
|
|
buf->output_width = res->width;
|
|
buf->output_height = res->height;
|
|
buf->read_mode = res->bin_mode;
|
|
buf->binning_factor_x = res->bin_factor_x;
|
|
buf->binning_factor_y = res->bin_factor_y;
|
|
buf->crop_horizontal_start = res->horizontal_start;
|
|
buf->crop_horizontal_end = res->horizontal_end;
|
|
buf->crop_vertical_start = res->vertical_start;
|
|
buf->crop_vertical_end = res->vertical_end;
|
|
|
|
dev_info(&client->dev, "%s h start %d end %d v start %d end %d "
|
|
"frame_length_line %d line_length_pck %d "
|
|
" read mode %d binning_x %d binning_y %d\n", __func__,
|
|
buf->crop_horizontal_start, buf->crop_horizontal_end,
|
|
buf->crop_vertical_start, buf->crop_vertical_end,
|
|
buf->frame_length_lines, buf->line_length_pck,
|
|
buf->read_mode, buf->binning_factor_x, buf->binning_factor_y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_get_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
fmt->width = dev->hm2056_raw_res[dev->fmt_idx].width;
|
|
fmt->height = dev->hm2056_raw_res[dev->fmt_idx].height;
|
|
fmt->code = V4L2_MBUS_FMT_SGRBG8_1X8;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_set_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
struct camera_mipi_info *hm2056_raw_info = NULL;
|
|
int ret;
|
|
int index;
|
|
u32 width;
|
|
u32 height;
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
width = fmt->width;
|
|
height = fmt->height;
|
|
hm2056_raw_info = v4l2_get_subdev_hostdata(sd);
|
|
|
|
if (hm2056_raw_info == NULL)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
hm2056_raw_try_res(sd, &width, &height);
|
|
|
|
dev_info(&client->dev, "%s %d x %d => %d x %d\n", __func__, fmt->width, fmt->height, width, height);
|
|
|
|
dev->fmt_idx = hm2056_raw_to_res(sd, width, height);
|
|
if (dev->fmt_idx == -1) {
|
|
dev->fmt_idx = 0;
|
|
mutex_unlock(&dev->input_lock);
|
|
dev_err(&client->dev, "%s no resolution found\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = hm2056_raw_write_reg_array(client, dev->hm2056_raw_res[dev->fmt_idx].regs);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (hm2056_raw_set_suspend(sd)) {
|
|
mutex_unlock(&dev->input_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Marked current sensor res as being "used"
|
|
*
|
|
* REVISIT: We don't need to use an "used" field on each mode
|
|
* list entry to know which mode is selected. If this
|
|
* information is really necessary, how about to use a single
|
|
* variable on sensor dev struct?
|
|
*/
|
|
for (index = 0; index < dev->n_res; index++) {
|
|
if ((width == dev->hm2056_raw_res[index].width) &&
|
|
(height == dev->hm2056_raw_res[index].height)) {
|
|
dev->hm2056_raw_res[index].used = 1;
|
|
continue;
|
|
}
|
|
dev->hm2056_raw_res[index].used = 0;
|
|
}
|
|
|
|
ret = hm2056_raw_get_intg_factor(client, hm2056_raw_info,
|
|
&dev->hm2056_raw_res[dev->fmt_idx]);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
dev_err(&client->dev, "failed to get integration_factor\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
fmt->width = width;
|
|
fmt->height = height;
|
|
fmt->code = V4L2_MBUS_FMT_SGRBG8_1X8;
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_q_exposure(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
u32 integration_time_h = 0;
|
|
u32 integration_time_l = 0;
|
|
int ret = 0;
|
|
|
|
ret = hm2056_raw_read_reg(client, HM2056_8BIT, HM2056_REG_INTEGRATION_TIME_H, &integration_time_h);
|
|
if (ret) {
|
|
v4l2_err(client, "%s: read HM2056_REG_INTEGRATION_TIME_H error %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = hm2056_raw_read_reg(client, HM2056_8BIT, HM2056_REG_INTEGRATION_TIME_L, &integration_time_l);
|
|
if (ret) {
|
|
v4l2_err(client, "%s: read HM2056_REG_INTEGRATION_TIME_L error %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
*val = integration_time_l | (integration_time_h << 8);
|
|
return 0;
|
|
}
|
|
|
|
static long hm2056_raw_s_exposure(struct v4l2_subdev *sd,
|
|
struct atomisp_exposure *exposure)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret = 0;
|
|
u8 analog_gain = 0;
|
|
u8 digital_gain = 0;
|
|
u32 reg_val_l, reg_val_h;
|
|
unsigned int frame_length_lines = 0;
|
|
unsigned int real_gain = exposure->gain[0];
|
|
|
|
// dev_info(&client->dev, "%s(%d %d %d %d)\n", __func__,
|
|
// exposure->integration_time[0], exposure->integration_time[1],
|
|
// exposure->gain[0], exposure->gain[1]);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
frame_length_lines = dev->hm2056_raw_res[dev->fmt_idx].frame_length_lines;
|
|
|
|
if (frame_length_lines < exposure->integration_time[0]) {
|
|
// dev_info(&client->dev, "%s update frame_length_line %d "
|
|
// "integration_time %d\n", __func__, frame_length_lines,
|
|
// exposure->integration_time[0]);
|
|
frame_length_lines = exposure->integration_time[0] + 5;
|
|
}
|
|
|
|
reg_val_l = (frame_length_lines - dev->hm2056_raw_res[dev->fmt_idx].height) & 0xFF;
|
|
reg_val_h = ((frame_length_lines - dev->hm2056_raw_res[dev->fmt_idx].height) & 0xFF00) >> 8;
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_BLANKING_ROW_H, reg_val_h);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_BLANKING_ROW_H error %x\n", __func__, reg_val_h);
|
|
return ret;
|
|
}
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_BLANKING_ROW_L, reg_val_l);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_BLANKING_ROW_L error %x\n", __func__, reg_val_l);
|
|
return ret;
|
|
}
|
|
|
|
reg_val_h = (exposure->integration_time[0] & 0xFF00) >> 8;
|
|
reg_val_l = exposure->integration_time[0] & 0xFF;
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_INTEGRATION_TIME_H, reg_val_h);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_INTEGRATION_TIME_H error %x\n", __func__, reg_val_h);
|
|
return ret;
|
|
}
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_INTEGRATION_TIME_L, reg_val_l);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_INTEGRATION_TIME_L error %x\n", __func__, reg_val_l);
|
|
return ret;
|
|
}
|
|
|
|
// DIT request the AG and DG can't more than 4x, so the max gain value will be 4xAG*4xDG=16x
|
|
if (16 <= real_gain && real_gain < 32) {
|
|
analog_gain = 0x0;
|
|
digital_gain = real_gain * 4;
|
|
} else if (32 <= real_gain && real_gain < 64) {
|
|
analog_gain = 0x1;
|
|
digital_gain = real_gain * 2;
|
|
} else if (64 <= real_gain && real_gain <= 256) {
|
|
analog_gain = 0x2;
|
|
digital_gain = real_gain;
|
|
} else
|
|
v4l2_err(client, "unsupported gain value\n");
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_AGAIN, analog_gain);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_AGAIN error %x\n", __func__, analog_gain);
|
|
return ret;
|
|
}
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_DGAIN, digital_gain);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_DGAIN error %x\n", __func__, digital_gain);
|
|
return ret;
|
|
}
|
|
|
|
ret = hm2056_raw_write_reg(client, HM2056_8BIT, HM2056_REG_COMMAND_UPDATE, 1);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "%s: write HM2056_REG_COMMAND_UPDATE error\n", __func__);
|
|
return ret;
|
|
}
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static long hm2056_raw_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
|
{
|
|
switch (cmd) {
|
|
case ATOMISP_IOC_S_EXPOSURE:
|
|
return hm2056_raw_s_exposure(sd, arg);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_g_bin_factor_x(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
*val = dev->hm2056_raw_res[dev->fmt_idx].bin_factor_x - 1;
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_g_bin_factor_y(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
*val = dev->hm2056_raw_res[dev->fmt_idx].bin_factor_y - 1;
|
|
return 0;
|
|
}
|
|
|
|
static struct hm2056_raw_control hm2056_raw_controls[] = {
|
|
{
|
|
.qc = {
|
|
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "exposure",
|
|
.minimum = 0x0,
|
|
.maximum = 0xffff,
|
|
.step = 0x01,
|
|
.default_value = 0x00,
|
|
.flags = 0,
|
|
},
|
|
.query = hm2056_raw_q_exposure,
|
|
},
|
|
{
|
|
.qc = {
|
|
.id = V4L2_CID_BIN_FACTOR_HORZ,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "horizontal binning factor",
|
|
.minimum = 0,
|
|
.maximum = 2,
|
|
.step = 1,
|
|
.default_value = 0,
|
|
.flags = 0,
|
|
},
|
|
.query = hm2056_raw_g_bin_factor_x,
|
|
},
|
|
{
|
|
.qc = {
|
|
.id = V4L2_CID_BIN_FACTOR_VERT,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "vertical binning factor",
|
|
.minimum = 0,
|
|
.maximum = 2,
|
|
.step = 1,
|
|
.default_value = 0,
|
|
.flags = 0,
|
|
},
|
|
.query = hm2056_raw_g_bin_factor_y,
|
|
},
|
|
};
|
|
#define N_CONTROLS (ARRAY_SIZE(hm2056_raw_controls))
|
|
|
|
static struct hm2056_raw_control *hm2056_raw_find_control(__u32 id)
|
|
{
|
|
int i;
|
|
for (i = 0; i < N_CONTROLS; i++) {
|
|
if (hm2056_raw_controls[i].qc.id == id) {
|
|
return &hm2056_raw_controls[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int hm2056_raw_detect(struct hm2056_raw_device *dev, struct i2c_client *client)
|
|
{
|
|
struct i2c_adapter *adapter = client->adapter;
|
|
u32 reg_h, reg_l;
|
|
u32 chip_id;
|
|
int ret;
|
|
|
|
ATD_hm2056_raw_status = 0; //Add for ATD command+++
|
|
|
|
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
|
|
dev_err(&client->dev, "%s: i2c error", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = hm2056_raw_read_reg(client, HM2056_8BIT, HM2056_REG_CHIP_ID_H, ®_h);
|
|
if (ret) {
|
|
v4l2_err(client, "%s: fail to read HM2056_REG_CHIP_ID_H\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = hm2056_raw_read_reg(client, HM2056_8BIT, HM2056_REG_CHIP_ID_L, ®_l);
|
|
if (ret) {
|
|
v4l2_err(client, "%s: fail to read HM2056_REG_CHIP_ID_L\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
chip_id = reg_l | (reg_h << 8);
|
|
|
|
if (chip_id != HM2056_MOD_ID) {
|
|
dev_err(&client->dev, "%s: failed: client->addr = %x, id read %x\n",
|
|
__func__, client->addr, chip_id);
|
|
return -ENODEV;
|
|
}
|
|
else {
|
|
dev_info(&client->dev, "%s sensor ID is 0x%x\n", __func__, chip_id);
|
|
}
|
|
|
|
ATD_hm2056_raw_status = 1; //Add for ATD command+++
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hm2056_raw_s_config(struct v4l2_subdev *sd, int irq, void *platform_data)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
if (NULL == platform_data)
|
|
return -ENODEV;
|
|
|
|
dev->platform_data =
|
|
(struct camera_sensor_platform_data *)platform_data;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (dev->platform_data->platform_init) {
|
|
ret = dev->platform_data->platform_init(client);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "hm2056_raw platform init err\n");
|
|
return ret;
|
|
}
|
|
}
|
|
ret = power_up(sd);
|
|
if (ret) {
|
|
v4l2_err(client, "hm2056_raw power-up err");
|
|
//goto fail_detect;
|
|
}
|
|
|
|
ret = dev->platform_data->csi_cfg(sd, 1);
|
|
if (ret) {
|
|
v4l2_err(client, "hm2056_raw config csi err");
|
|
goto fail_csi_cfg;
|
|
}
|
|
|
|
/* config & detect sensor */
|
|
ret = hm2056_raw_detect(dev, client);
|
|
if (ret) {
|
|
v4l2_err(client, "hm2056_raw_detect err s_config.\n");
|
|
//goto fail_detect;
|
|
}
|
|
|
|
ret = power_down(sd);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
v4l2_err(client, "hm2056_raw power down err");
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return 0;
|
|
|
|
fail_csi_cfg:
|
|
dev->platform_data->csi_cfg(sd, 0);
|
|
//fail_detect:
|
|
power_down(sd);
|
|
mutex_unlock(&dev->input_lock);
|
|
dev_err(&client->dev, "sensor power-gating failed\n");
|
|
return ret;
|
|
}
|
|
|
|
static int hm2056_raw_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
|
|
{
|
|
struct hm2056_raw_control *ctrl = hm2056_raw_find_control(qc->id);
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (ctrl == NULL)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
*qc = ctrl->qc;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_s_parm(struct v4l2_subdev *sd,
|
|
struct v4l2_streamparm *param)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (!param)
|
|
return -EINVAL;
|
|
|
|
dev->run_mode = param->parm.capture.capturemode;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
switch (dev->run_mode) {
|
|
case CI_MODE_VIDEO:
|
|
dev->hm2056_raw_res = hm2056_raw_res_video;
|
|
dev->n_res = N_RES_VIDEO;
|
|
break;
|
|
case CI_MODE_STILL_CAPTURE:
|
|
dev->hm2056_raw_res = hm2056_raw_res_still;
|
|
dev->n_res = N_RES_STILL;
|
|
break;
|
|
default:
|
|
dev->hm2056_raw_res = hm2056_raw_res_preview;
|
|
dev->n_res = N_RES_PREVIEW;
|
|
}
|
|
|
|
dev->fmt_idx = 0;
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
|
{
|
|
struct hm2056_raw_control *octrl = hm2056_raw_find_control(ctrl->id);
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
int ret;
|
|
|
|
if (octrl == NULL)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
ret = octrl->query(sd, &ctrl->value);
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hm2056_raw_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
|
{
|
|
struct hm2056_raw_control *octrl = hm2056_raw_find_control(ctrl->id);
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
int ret;
|
|
|
|
if (!octrl || !octrl->tweak)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
ret = octrl->tweak(sd, ctrl->value);
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hm2056_raw_s_stream(struct v4l2_subdev *sd, int enable)
|
|
{
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
dev_info(&client->dev, "%s enable %d\n", __func__, enable);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (enable) {
|
|
ret = hm2056_raw_set_streaming(sd);
|
|
} else {
|
|
ret = hm2056_raw_set_suspend(sd);
|
|
}
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
hm2056_raw_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize)
|
|
{
|
|
unsigned int index = fsize->index;
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (index >= dev->n_res)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
|
|
fsize->discrete.width = dev->hm2056_raw_res[index].width;
|
|
fsize->discrete.height = dev->hm2056_raw_res[index].height;
|
|
fsize->reserved[0] = dev->hm2056_raw_res[index].used;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_enum_frameintervals(struct v4l2_subdev *sd,
|
|
struct v4l2_frmivalenum *fival)
|
|
{
|
|
unsigned int index = fival->index;
|
|
int i;
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (index >= dev->n_res)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
/* find out the first equal or bigger size */
|
|
for (i = 0; i < dev->n_res; i++) {
|
|
if ((dev->hm2056_raw_res[i].width >= fival->width) &&
|
|
(dev->hm2056_raw_res[i].height >= fival->height))
|
|
break;
|
|
}
|
|
if (i == dev->n_res)
|
|
i--;
|
|
|
|
index = i;
|
|
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
|
|
fival->discrete.numerator = 1;
|
|
fival->discrete.denominator = dev->hm2056_raw_res[index].fps;
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hm2056_raw_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_HM2056_RAW, 0);
|
|
}
|
|
|
|
static int hm2056_raw_enum_mbus_code(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (!code || code->index >= MAX_FMTS)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
code->code = V4L2_MBUS_FMT_SGRBG8_1X8;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return code->code < 0 ? code->code : 0;
|
|
}
|
|
|
|
static int hm2056_raw_enum_frame_size(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_frame_size_enum *fse)
|
|
{
|
|
unsigned int index;
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (!fse)
|
|
return -EINVAL;
|
|
|
|
index = fse->index;
|
|
|
|
if (index >= dev->n_res)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
fse->min_width = dev->hm2056_raw_res[index].width;
|
|
fse->min_height = dev->hm2056_raw_res[index].height;
|
|
fse->max_width = dev->hm2056_raw_res[index].width;
|
|
fse->max_height = dev->hm2056_raw_res[index].height;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct v4l2_mbus_framefmt *
|
|
__hm2056_raw_get_pad_format(struct hm2056_raw_device *sensor,
|
|
struct v4l2_subdev_fh *fh, unsigned int pad,
|
|
enum v4l2_subdev_format_whence which)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
|
|
|
|
if (pad != 0) {
|
|
dev_err(&client->dev, "%s err. pad %x\n", __func__, pad);
|
|
return NULL;
|
|
}
|
|
|
|
switch (which) {
|
|
case V4L2_SUBDEV_FORMAT_TRY:
|
|
return v4l2_subdev_get_try_format(fh, pad);
|
|
case V4L2_SUBDEV_FORMAT_ACTIVE:
|
|
return &sensor->format;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
hm2056_raw_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct hm2056_raw_device *snr = to_hm2056_raw_sensor(sd);
|
|
struct v4l2_mbus_framefmt *format;
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
format = __hm2056_raw_get_pad_format(snr, fh, fmt->pad, fmt->which);
|
|
|
|
if (format == NULL)
|
|
return -EINVAL;
|
|
|
|
fmt->format = *format;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hm2056_raw_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct hm2056_raw_device *snr = to_hm2056_raw_sensor(sd);
|
|
struct v4l2_mbus_framefmt *format;
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
format = __hm2056_raw_get_pad_format(snr, fh, fmt->pad, fmt->which);
|
|
|
|
if (format == NULL)
|
|
return -EINVAL;
|
|
|
|
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
|
snr->format = fmt->format;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
|
|
{
|
|
struct hm2056_raw_device *dev = to_hm2056_raw_sensor(sd);
|
|
|
|
if (frames == NULL)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
*frames = dev->hm2056_raw_res[dev->fmt_idx].skip_frames;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
static const struct v4l2_subdev_video_ops hm2056_raw_video_ops = {
|
|
.s_parm = hm2056_raw_s_parm,
|
|
.try_mbus_fmt = hm2056_raw_try_mbus_fmt,
|
|
.s_mbus_fmt = hm2056_raw_set_mbus_fmt,
|
|
.g_mbus_fmt = hm2056_raw_get_mbus_fmt,
|
|
.s_stream = hm2056_raw_s_stream,
|
|
.enum_framesizes = hm2056_raw_enum_framesizes,
|
|
.enum_frameintervals = hm2056_raw_enum_frameintervals,
|
|
};
|
|
|
|
static struct v4l2_subdev_sensor_ops hm2056_raw_sensor_ops = {
|
|
.g_skip_frames = hm2056_raw_g_skip_frames,
|
|
};
|
|
|
|
static const struct v4l2_subdev_core_ops hm2056_raw_core_ops = {
|
|
.g_chip_ident = hm2056_raw_g_chip_ident,
|
|
.queryctrl = hm2056_raw_queryctrl,
|
|
.g_ctrl = hm2056_raw_g_ctrl,
|
|
.s_ctrl = hm2056_raw_s_ctrl,
|
|
.s_power = hm2056_raw_s_power,
|
|
.ioctl = hm2056_raw_ioctl,
|
|
};
|
|
|
|
/* REVISIT: Do we need pad operations? */
|
|
static const struct v4l2_subdev_pad_ops hm2056_raw_pad_ops = {
|
|
.enum_mbus_code = hm2056_raw_enum_mbus_code,
|
|
.enum_frame_size = hm2056_raw_enum_frame_size,
|
|
.get_fmt = hm2056_raw_get_pad_format,
|
|
.set_fmt = hm2056_raw_set_pad_format,
|
|
};
|
|
|
|
static const struct v4l2_subdev_ops hm2056_raw_ops = {
|
|
.core = &hm2056_raw_core_ops,
|
|
.video = &hm2056_raw_video_ops,
|
|
.pad = &hm2056_raw_pad_ops,
|
|
.sensor = &hm2056_raw_sensor_ops,
|
|
};
|
|
|
|
static int hm2056_raw_remove(struct i2c_client *client)
|
|
{
|
|
struct hm2056_raw_device *dev;
|
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
|
|
dev = container_of(sd, struct hm2056_raw_device, sd);
|
|
|
|
dev->platform_data->csi_cfg(sd, 0);
|
|
|
|
if (dev->platform_data->platform_deinit)
|
|
dev->platform_data->platform_deinit();
|
|
v4l2_device_unregister_subdev(sd);
|
|
media_entity_cleanup(&dev->sd.entity);
|
|
kfree(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int hm2056_raw_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct hm2056_raw_device *dev;
|
|
int ret;
|
|
|
|
/* Setup sensor configuration structure */
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev) {
|
|
dev_err(&client->dev, "out of memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
mutex_init(&dev->input_lock);
|
|
dev->fmt_idx = 0;
|
|
dev->hm2056_raw_res = hm2056_raw_res_preview;
|
|
dev->n_res = N_RES_STILL;
|
|
|
|
//Add for ATD command+++
|
|
dev->sensor_i2c_attribute.attrs = hm2056_raw_attributes;
|
|
|
|
// Register sysfs hooks
|
|
ret = sysfs_create_group(&client->dev.kobj, &dev->sensor_i2c_attribute);
|
|
if (ret) {
|
|
dev_err(&client->dev, "Not able to create the sysfs\n");
|
|
return ret;
|
|
}
|
|
//Add for ATD command---
|
|
|
|
v4l2_i2c_subdev_init(&dev->sd, client, &hm2056_raw_ops);
|
|
|
|
if (client->dev.platform_data) {
|
|
ret = hm2056_raw_s_config(&dev->sd, client->irq,
|
|
client->dev.platform_data);
|
|
if (ret) {
|
|
v4l2_device_unregister_subdev(&dev->sd);
|
|
kfree(dev);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
dev->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
dev->format.code = V4L2_MBUS_FMT_SGRBG8_1X8;
|
|
dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
|
|
|
ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0);
|
|
if (ret) {
|
|
hm2056_raw_remove(client);
|
|
}
|
|
|
|
main_sd = &dev->sd; //Add for ATD command+++
|
|
|
|
return ret;
|
|
}
|
|
|
|
MODULE_DEVICE_TABLE(i2c, hm2056_raw_id);
|
|
|
|
static struct i2c_driver hm2056_raw_driver = {
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = HM2056_NAME,
|
|
},
|
|
.probe = hm2056_raw_probe,
|
|
.remove = hm2056_raw_remove,
|
|
.id_table = hm2056_raw_id,
|
|
};
|
|
|
|
static __init int init_hm2056_raw(void)
|
|
{
|
|
return i2c_add_driver(&hm2056_raw_driver);
|
|
}
|
|
|
|
static __exit void exit_hm2056_raw(void)
|
|
{
|
|
i2c_del_driver(&hm2056_raw_driver);
|
|
}
|
|
|
|
module_init(init_hm2056_raw);
|
|
module_exit(exit_hm2056_raw);
|
|
|
|
MODULE_AUTHOR("Hayden Huang <hayden.huang@intel.com>");
|
|
MODULE_DESCRIPTION("A low-level driver for Himax HM2056 sensors");
|
|
MODULE_LICENSE("GPL");
|