3489 lines
86 KiB
C
3489 lines
86 KiB
C
/*
|
|
* Copyright (c) 2014 Intel Corporation. All Rights Reserved.
|
|
*
|
|
* Partially based on m-5mols kernel driver,
|
|
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
|
|
*
|
|
* Partially based on jc_v4l2 kernel driver from http://opensource.samsung.com
|
|
* Copyright (c) 2011, Code Aurora Forum. 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 <asm/intel-mid.h>
|
|
#include <asm/irq.h>
|
|
#include <linux/atomisp_platform.h>
|
|
#include <linux/atomisp.h>
|
|
#include <media/m10mo_atomisp.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/io.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/wait.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-chip-ident.h>
|
|
#include <media/v4l2-device.h>
|
|
#include "m10mo.h"
|
|
|
|
/* cross component debug message flag */
|
|
int dbglvl = 0;
|
|
module_param(dbglvl, int, 0644);
|
|
MODULE_PARM_DESC(dbglvl, "debug message on/off (default:off)");
|
|
|
|
/*
|
|
* m10mo_read - I2C read function
|
|
* @reg: combination of size, category and command for the I2C packet
|
|
* @size: desired size of I2C packet
|
|
* @val: read value
|
|
*
|
|
* Returns 0 on success, or else negative errno.
|
|
*/
|
|
|
|
static int m10mo_read(struct v4l2_subdev *sd, u8 len, u8 category, u8 reg, u32 *val)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
unsigned char data[5];
|
|
struct i2c_msg msg[2];
|
|
unsigned char recv_data[len + 1];
|
|
int ret;
|
|
|
|
if (!client->adapter)
|
|
return -ENODEV;
|
|
|
|
if (len != 1 && len != 2 && len != 4) {
|
|
dev_err(&client->dev, "Wrong data size\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = sizeof(data);
|
|
msg[0].buf = data;
|
|
|
|
data[0] = 5;
|
|
data[1] = M10MO_BYTE_READ;
|
|
data[2] = category;
|
|
data[3] = reg;
|
|
data[4] = len;
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = len + 1;
|
|
msg[1].buf = recv_data;
|
|
|
|
/* isp firmware becomes stable during this time*/
|
|
usleep_range(200, 200);
|
|
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
|
|
if (ret == 2) {
|
|
if (len == 0x01)
|
|
*val = recv_data[1];
|
|
else if (len == 0x02)
|
|
*val = recv_data[1] << 8 | recv_data[2];
|
|
else
|
|
*val = recv_data[1] << 24 | recv_data[2] << 16
|
|
| recv_data[3] << 8 | recv_data[4];
|
|
}
|
|
|
|
dev_dbg(&client->dev,
|
|
"%s len :%d cat, reg, val: 0x%02x, 0x%02x, 0x%02x\n",
|
|
__func__, len, category, reg, *val);
|
|
|
|
return (ret == 2) ? 0 : -EIO;
|
|
}
|
|
|
|
/**
|
|
* m10mo_write - I2C command write function
|
|
* @reg: combination of size, category and command for the I2C packet
|
|
* @val: value to write
|
|
*
|
|
* Returns 0 on success, or else negative errno.
|
|
*/
|
|
static int m10mo_write(struct v4l2_subdev *sd, u8 len, u8 category, u8 reg, u32 val)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
u8 data[len + 4];
|
|
struct i2c_msg msg;
|
|
int ret;
|
|
const int num_msg = 1;
|
|
|
|
if (!client->adapter)
|
|
return -ENODEV;
|
|
|
|
if (len != 1 && len != 2 && len != 4) {
|
|
dev_err(&client->dev, "Wrong data size\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_dbg(&client->dev,
|
|
"%s len :%d cat, reg, val: 0x%02x, 0x%02x, 0x%02x\n",
|
|
__func__, len, category, reg, val);
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = sizeof(data);
|
|
msg.buf = data;
|
|
|
|
data[0] = msg.len;
|
|
data[1] = M10MO_BYTE_WRITE;
|
|
data[2] = category;
|
|
data[3] = reg;
|
|
switch (len) {
|
|
case 1:
|
|
data[4] = val;
|
|
break;
|
|
case 2:
|
|
data[4] = ((val >> 8) & 0xFF);
|
|
data[5] = (val & 0xFF);
|
|
break;
|
|
case 4:
|
|
data[4] = ((val >> 24) & 0xFF);
|
|
data[5] = ((val >> 16) & 0xFF);
|
|
data[6] = ((val >> 8) & 0xFF);
|
|
data[7] = (val & 0xFF);
|
|
break;
|
|
default:
|
|
/* No possible to happen - len is already validated */
|
|
break;
|
|
}
|
|
|
|
/* isp firmware becomes stable during this time*/
|
|
usleep_range(200, 200);
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
|
|
dev_dbg(&client->dev, "Write reg. Category=0x%02X Reg=0x%02X Value=0x%X ret=%s\n",
|
|
category, reg, val, (ret == 1) ? "OK" : "Error");
|
|
return ret == num_msg ? 0 : -EIO;
|
|
}
|
|
|
|
int m10mo_writeb(struct v4l2_subdev *sd, u8 category, u8 reg, u32 val)
|
|
{
|
|
return m10mo_write(sd, 1, category, reg, val);
|
|
}
|
|
|
|
int m10mo_writew(struct v4l2_subdev *sd, u8 category, u8 reg, u32 val)
|
|
{
|
|
return m10mo_write(sd, 2, category, reg, val);
|
|
}
|
|
|
|
int m10mo_writel(struct v4l2_subdev *sd, u8 category, u8 reg, u32 val)
|
|
{
|
|
return m10mo_write(sd, 4, category, reg, val);
|
|
}
|
|
|
|
int m10mo_readb(struct v4l2_subdev *sd, u8 category, u8 reg, u32 *val)
|
|
{
|
|
return m10mo_read(sd, 1, category, reg, val);
|
|
}
|
|
|
|
int m10mo_readw(struct v4l2_subdev *sd, u8 category, u8 reg, u32 *val)
|
|
{
|
|
return m10mo_read(sd, 2, category, reg, val);
|
|
}
|
|
|
|
int m10mo_readl(struct v4l2_subdev *sd, u8 category, u8 reg, u32 *val)
|
|
{
|
|
return m10mo_read(sd, 4, category, reg, val);
|
|
}
|
|
|
|
int m10mo_memory_write(struct v4l2_subdev *sd, u8 cmd, u16 len, u32 addr, u8 *val)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct m10mo_device *m10mo_dev = to_m10mo_sensor(sd);
|
|
struct i2c_msg msg;
|
|
u8 *data = m10mo_dev->message_buffer;
|
|
int i, ret;
|
|
|
|
dev_dbg(&client->dev, "Write mem. cmd=0x%02X len=%d addr=0x%X\n", cmd, len, addr);
|
|
|
|
if (!client->adapter)
|
|
return -ENODEV;
|
|
|
|
if ((len + 8) > sizeof(m10mo_dev->message_buffer))
|
|
return -ENOMEM;
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = len + 8;
|
|
msg.buf = data;
|
|
|
|
/* high byte goes out first */
|
|
data[0] = 0x00;
|
|
data[1] = cmd;
|
|
data[2] = (u8)((addr >> 24) & 0xFF);
|
|
data[3] = (u8)((addr >> 16) & 0xFF);
|
|
data[4] = (u8)((addr >> 8) & 0xFF);
|
|
data[5] = (u8)(addr & 0xFF);
|
|
data[6] = len >> 8;
|
|
data[7] = len;
|
|
/* Payload starts at offset 8 */
|
|
memcpy(data + 8, val, len);
|
|
|
|
usleep_range(200, 200);
|
|
|
|
for (i = M10MO_I2C_RETRY; i; i--) {
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret == 1) {
|
|
return 0;
|
|
}
|
|
msleep(20);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_memory_read(struct v4l2_subdev *sd, u16 len, u32 addr, u8 *val)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct m10mo_device *m10mo_dev = to_m10mo_sensor(sd);
|
|
struct i2c_msg msg;
|
|
unsigned char data[8];
|
|
u8 *recv_data = m10mo_dev->message_buffer;
|
|
int i, err = 0;
|
|
|
|
if (!client->adapter)
|
|
return -ENODEV;
|
|
|
|
if (len <= 0)
|
|
return -EINVAL;
|
|
|
|
if ((len + 3) > sizeof(m10mo_dev->message_buffer))
|
|
return -ENOMEM;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = sizeof(data);
|
|
msg.buf = data;
|
|
|
|
/* high byte goes out first */
|
|
data[0] = 0x00;
|
|
data[1] = 0x03;
|
|
data[2] = (addr >> 24) & 0xFF;
|
|
data[3] = (addr >> 16) & 0xFF;
|
|
data[4] = (addr >> 8) & 0xFF;
|
|
data[5] = addr & 0xFF;
|
|
data[6] = (len >> 8) & 0xFF;
|
|
data[7] = len & 0xFF;
|
|
|
|
for (i = M10MO_I2C_RETRY; i; i--) {
|
|
err = i2c_transfer(client->adapter, &msg, 1);
|
|
if (err == 1)
|
|
break;
|
|
msleep(20);
|
|
}
|
|
|
|
if (err == 0)
|
|
return -EIO;
|
|
|
|
if (err != 1)
|
|
return err;
|
|
|
|
msg.flags = I2C_M_RD;
|
|
msg.len = len + 3;
|
|
msg.buf = recv_data;
|
|
for (i = M10MO_I2C_RETRY; i; i--) {
|
|
err = i2c_transfer(client->adapter, &msg, 1);
|
|
if (err == 1)
|
|
break;
|
|
msleep(20);
|
|
}
|
|
|
|
if (err == 0)
|
|
return -EIO;
|
|
|
|
if (err != 1)
|
|
return err;
|
|
|
|
if (len != (recv_data[1] << 8 | recv_data[2])) {
|
|
dev_err(&client->dev,
|
|
"expected length %d, but return length %d\n",
|
|
len, recv_data[1] << 8 | recv_data[2]);
|
|
return -EIO;
|
|
}
|
|
|
|
memcpy(val, recv_data + 3, len);
|
|
|
|
dev_dbg(&client->dev, "Read mem. len=%d addr=0x%X\n", len, addr);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* m10mo_setup_flash_controller - initialize flash controller
|
|
*
|
|
* Flash controller requires additional setup before
|
|
* the use.
|
|
*/
|
|
int m10mo_setup_flash_controller(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
u8 data = 0x7F;
|
|
int res;
|
|
|
|
res = m10mo_memory_write(sd, M10MO_MEMORY_WRITE_8BIT,
|
|
1, 0x13000005, &data);
|
|
if (res < 0)
|
|
dev_err(&client->dev, "Setup flash controller failed\n");
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* m10mo_wait_interrupt - Clear interrupt pending bits and unmask interrupts
|
|
*
|
|
* Before writing desired interrupt value the INT_FACTOR register should
|
|
* be read to clear pending interrupts.
|
|
*/
|
|
|
|
int m10mo_request_mode_change(struct v4l2_subdev *sd, u8 requested_mode)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret = 0;
|
|
|
|
dev->requested_mode = requested_mode;
|
|
|
|
switch (dev->requested_mode) {
|
|
case M10MO_FLASH_WRITE_MODE:
|
|
break;
|
|
case M10MO_PARAM_SETTING_MODE:
|
|
ret = m10mo_write(sd, 1, CATEGORY_FLASHROM, FLASH_CAM_START, 0x01);
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Unable to change to PARAM_SETTING_MODE\n");
|
|
break;
|
|
case M10MO_PARAMETER_MODE:
|
|
ret = m10mo_write(sd, 1, CATEGORY_SYSTEM, SYSTEM_SYSMODE, 0x01);
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Unable to change to PARAMETER MODE\n");
|
|
break;
|
|
case M10MO_MONITOR_MODE_PANORAMA:
|
|
case M10MO_MONITOR_MODE_ZSL:
|
|
case M10MO_MONITOR_MODE:
|
|
case M10MO_MONITOR_MODE_HIGH_SPEED:
|
|
ret = m10mo_write(sd, 1, CATEGORY_SYSTEM, SYSTEM_SYSMODE, 0x02);
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Unable to change to MONITOR_MODE / ZSL \n");
|
|
break;
|
|
case M10MO_SINGLE_CAPTURE_MODE:
|
|
ret = m10mo_write(sd, 1, CATEGORY_SYSTEM, SYSTEM_SYSMODE, 0x03);
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Unable to change to SINGLE_CAPTURE_MODE\n");
|
|
break;
|
|
case M10MO_BURST_CAPTURE_MODE:
|
|
/* Set monitor type as burst capture. */
|
|
ret = m10mo_write(sd, 1, CATEGORY_PARAM,
|
|
MONITOR_TYPE, MONITOR_BURST);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,
|
|
"Unable to change to BURST_CAPTURE_MODE\n");
|
|
break;
|
|
}
|
|
ret = m10mo_write(sd, 1, CATEGORY_SYSTEM, SYSTEM_SYSMODE, 0x02);
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Unable to change to BURST_CAPTURE_MODE\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int is_m10mo_in_monitor_mode(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
if (dev->mode == M10MO_MONITOR_MODE_PANORAMA ||
|
|
dev->mode == M10MO_MONITOR_MODE_ZSL ||
|
|
dev->mode == M10MO_MONITOR_MODE ||
|
|
dev->mode == M10MO_MONITOR_MODE_HIGH_SPEED)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_set_monitor_parameters(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
int ret;
|
|
|
|
dev_info(&client->dev,
|
|
"%s: af_mode: 0x%x exe_mode: 0x%x posx: 0x%x, posy: 0x%x\n",
|
|
__func__, dev->monitor_params.af_mode,
|
|
dev->monitor_params.exe_mode,
|
|
dev->monitor_params.af_touch_posx,
|
|
dev->monitor_params.af_touch_posy);
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_mode,
|
|
dev->monitor_params.af_mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_writew(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_touch_posx,
|
|
dev->monitor_params.af_touch_posx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_writew(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_touch_posy,
|
|
dev->monitor_params.af_touch_posy);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_execution,
|
|
dev->monitor_params.exe_mode);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (dev->monitor_params.flash_mode == LED_TORCH)
|
|
ret = m10mo_writeb(sd, CATEGORY_LOGLEDFLASH,
|
|
LED_TORCH,
|
|
dev->monitor_params.torch);
|
|
else
|
|
ret = m10mo_writeb(sd, CATEGORY_LOGLEDFLASH,
|
|
FLASH_MODE,
|
|
dev->monitor_params.flash_mode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_wait_mode_change(struct v4l2_subdev *sd, u8 mode, u32 timeout)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
ret = wait_event_interruptible_timeout(dev->irq_waitq,
|
|
dev->mode == mode,
|
|
msecs_to_jiffies(timeout));
|
|
if (ret > 0) {
|
|
if (is_m10mo_in_monitor_mode(sd))
|
|
return m10mo_set_monitor_parameters(sd);
|
|
return 0;
|
|
} else if (ret == 0) {
|
|
dev_err(&client->dev, "m10mo_wait_mode_change timed out\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __m10mo_param_mode_set(struct v4l2_subdev *sd)
|
|
{
|
|
int ret;
|
|
|
|
ret = m10mo_request_mode_change(sd, M10MO_PARAMETER_MODE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_wait_mode_change(sd, M10MO_PARAMETER_MODE,
|
|
M10MO_INIT_TIMEOUT);
|
|
if (ret > 0)
|
|
ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_detect(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct m10mo_version *ver = &dev->ver;
|
|
int ret;
|
|
|
|
ret = m10mo_read(sd, 1, CATEGORY_SYSTEM, SYSTEM_CUSTOMER_CODE,
|
|
&ver->customer);
|
|
if (!ret)
|
|
ret = m10mo_read(sd, 1, CATEGORY_SYSTEM, SYSTEM_PROJECT_CODE,
|
|
&ver->project);
|
|
dev_info(&client->dev, "Customer/Project[0x%x/0x%x]\n", dev->ver.customer,
|
|
dev->ver.project);
|
|
return 0;
|
|
}
|
|
|
|
static int __m10mo_fw_start(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
/*
|
|
* Correct the pll value before fw start
|
|
*/
|
|
ret = m10mo_update_pll_setting(sd);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = m10mo_setup_flash_controller(sd);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = m10mo_request_mode_change(sd, M10MO_PARAM_SETTING_MODE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_wait_mode_change(sd, M10MO_PARAM_SETTING_MODE,
|
|
M10MO_INIT_TIMEOUT);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev, "Initialization timeout\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = m10mo_detect(sd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dev_info(&client->dev, "ISP Booted Successfully\n");
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_fw_start(struct v4l2_subdev *sd, u32 val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
ret = __m10mo_fw_start(sd);
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_af_mode(struct v4l2_subdev *sd, unsigned int val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
int ret = 0;
|
|
u32 cur_af_mode;
|
|
|
|
switch (val) {
|
|
case EXT_ISP_FOCUS_MODE_NORMAL:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_normal;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_MACRO:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_macro;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_TOUCH_AF:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_touch;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_PREVIEW_CAF:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_preview_caf;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_MOVIE_CAF:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_movie_caf;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_FACE_CAF:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_face_caf;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_TOUCH_MACRO:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_touch_macro;
|
|
break;
|
|
case EXT_ISP_FOCUS_MODE_TOUCH_CAF:
|
|
dev->monitor_params.af_mode = m10m0_af_parameters[id].af_touch_caf;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_m10mo_in_monitor_mode(sd))
|
|
return ret;
|
|
|
|
ret = m10mo_read(sd, 1, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_mode,
|
|
&cur_af_mode);
|
|
|
|
/*
|
|
* If af_mode has changed as expected already
|
|
* no need to set any more
|
|
*/
|
|
if (dev->monitor_params.af_mode == cur_af_mode)
|
|
return ret;
|
|
|
|
dev_info(&client->dev, "%s: In monitor mode, set AF mode to %d",
|
|
__func__, dev->monitor_params.af_mode);
|
|
|
|
/* We are in monitor mode already, */
|
|
/* af_mode can be applied immediately */
|
|
ret = m10mo_writeb(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_mode,
|
|
dev->monitor_params.af_mode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_af_execution(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
int ret = 0;
|
|
|
|
switch (val) {
|
|
case EXT_ISP_FOCUS_STOP:
|
|
dev->monitor_params.exe_mode = m10m0_af_parameters[id].af_stop;
|
|
break;
|
|
case EXT_ISP_FOCUS_SEARCH:
|
|
dev->monitor_params.exe_mode = m10m0_af_parameters[id].af_search;
|
|
break;
|
|
case EXT_ISP_PAN_FOCUSING:
|
|
dev->monitor_params.exe_mode = m10m0_af_parameters[id].af_pan_focusing;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (is_m10mo_in_monitor_mode(sd)) {
|
|
|
|
dev_info(&client->dev, "%s: In monitor mode, set AF exe_mode to %d",
|
|
__func__, dev->monitor_params.exe_mode);
|
|
|
|
/* We are in monitor mode already, */
|
|
/* exe_mode can be applied immediately */
|
|
ret = m10mo_writeb(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_execution,
|
|
dev->monitor_params.exe_mode);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_af_position_x(struct v4l2_subdev *sd, unsigned int x)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret = 0;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
|
|
dev->monitor_params.af_touch_posx = x;
|
|
|
|
if (is_m10mo_in_monitor_mode(sd)) {
|
|
dev_info(&client->dev,
|
|
"%s: In monitor mode, set AF touch X pos to 0x%x",
|
|
__func__, dev->monitor_params.af_touch_posx);
|
|
|
|
/* Set X Position */
|
|
ret = m10mo_writew(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_touch_posx,
|
|
dev->monitor_params.af_touch_posx);
|
|
}
|
|
|
|
if (ret)
|
|
dev_err(&client->dev, "AutoFocus position x failed %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_af_position_y(struct v4l2_subdev *sd, unsigned int y)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret = 0;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
|
|
dev->monitor_params.af_touch_posy = y;
|
|
|
|
if (is_m10mo_in_monitor_mode(sd)) {
|
|
dev_info(&client->dev,
|
|
"%s: In monitor mode, set AF touch Y pos to 0x%x",
|
|
__func__, dev->monitor_params.af_touch_posy);
|
|
|
|
/* Set Y Position */
|
|
ret = m10mo_writew(sd, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_touch_posy,
|
|
dev->monitor_params.af_touch_posy);
|
|
}
|
|
|
|
if (ret)
|
|
dev_err(&client->dev, "AutoFocus position y failed %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Because of different m10m0 firmwares and parameter values, transform
|
|
the values to the macro definition */
|
|
static u32 m10mo_af_parameter_transform(struct v4l2_subdev *sd, u32 val)
|
|
{
|
|
u32 ret = 0xffffffff;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
|
|
if (val == m10m0_af_parameters[id].caf_status_focusing) {
|
|
ret = CAF_STATUS_FOCUSING;
|
|
} else if (val == m10m0_af_parameters[id].caf_status_success) {
|
|
ret = CAF_STATUS_SUCCESS;
|
|
} else if (val == m10m0_af_parameters[id].caf_status_fail) {
|
|
ret = CAF_STATUS_FAIL;
|
|
} else if (val == m10m0_af_parameters[id].caf_status_restart_check) {
|
|
ret = CAF_STATUS_RESTART_CHECK;
|
|
} else if (val == m10m0_af_parameters[id].af_status_invalid) {
|
|
ret = AF_STATUS_INVALID;
|
|
} else if (val == m10m0_af_parameters[id].af_status_focusing) {
|
|
ret = AF_STATUS_FOCUSING;
|
|
} else if (val == m10m0_af_parameters[id].af_status_success) {
|
|
ret = AF_STATUS_SUCCESS;
|
|
} else if (val == m10m0_af_parameters[id].af_status_fail) {
|
|
ret = AF_STATUS_FAIL;
|
|
} else if (val == m10m0_af_parameters[id].af_normal) {
|
|
ret = AF_NORMAL;
|
|
} else if (val == m10m0_af_parameters[id].af_macro) {
|
|
ret = AF_MACRO;
|
|
} else if (val == m10m0_af_parameters[id].af_touch) {
|
|
ret = AF_TOUCH;
|
|
} else if (val == m10m0_af_parameters[id].af_preview_caf) {
|
|
ret = AF_PREVIEW_CAF;
|
|
} else if (val == m10m0_af_parameters[id].af_movie_caf) {
|
|
ret = AF_MOVIE_CAF;
|
|
} else if (val == m10m0_af_parameters[id].af_face_caf) {
|
|
ret = AF_FACE_CAF;
|
|
} else if (val == m10m0_af_parameters[id].af_touch_macro) {
|
|
ret = AF_TOUCH_MACRO;
|
|
} else if (val == m10m0_af_parameters[id].af_touch_caf) {
|
|
ret = AF_TOUCH_CAF;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_get_caf_status(struct v4l2_subdev *sd, unsigned int *status)
|
|
{
|
|
int ret;
|
|
u32 af_result;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
|
|
ret = m10mo_read(sd, 1, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_result, &af_result);
|
|
if (ret)
|
|
return ret;
|
|
|
|
af_result = m10mo_af_parameter_transform(sd, af_result);
|
|
|
|
switch (af_result) {
|
|
case CAF_STATUS_FOCUSING:
|
|
*status = EXT_ISP_CAF_STATUS_FOCUSING;
|
|
break;
|
|
case CAF_STATUS_SUCCESS:
|
|
*status = EXT_ISP_CAF_STATUS_SUCCESS;
|
|
break;
|
|
case CAF_STATUS_FAIL:
|
|
*status = EXT_ISP_CAF_STATUS_FAIL;
|
|
break;
|
|
case CAF_STATUS_RESTART_CHECK:
|
|
*status = EXT_ISP_CAF_RESTART_CHECK;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_get_af_status(struct v4l2_subdev *sd, unsigned int *status)
|
|
{
|
|
int ret;
|
|
u32 af_result;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
|
|
ret = m10mo_read(sd, 1, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_result, &af_result);
|
|
if (ret)
|
|
return ret;
|
|
|
|
af_result = m10mo_af_parameter_transform(sd, af_result);
|
|
|
|
switch (af_result) {
|
|
case AF_STATUS_INVALID:
|
|
*status = EXT_ISP_AF_STATUS_INVALID;
|
|
break;
|
|
case AF_STATUS_FOCUSING:
|
|
*status = EXT_ISP_AF_STATUS_FOCUSING;
|
|
break;
|
|
case AF_STATUS_SUCCESS:
|
|
*status = EXT_ISP_AF_STATUS_SUCCESS;
|
|
break;
|
|
case AF_STATUS_FAIL:
|
|
*status = EXT_ISP_AF_STATUS_FAIL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Adding this as requested by HAL. Can be removed if HAL is saving af mode */
|
|
static int m10mo_get_af_mode(struct v4l2_subdev *sd, unsigned int *status)
|
|
{
|
|
int ret;
|
|
u32 af_mode;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int id = M10MO_GET_FOCUS_MODE(dev->fw_type);
|
|
|
|
ret = m10mo_read(sd, 1, CATEGORY_LENS,
|
|
m10m0_af_parameters[id].af_result, &af_mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
af_mode = m10mo_af_parameter_transform(sd, af_mode);
|
|
|
|
switch (af_mode) {
|
|
case AF_NORMAL:
|
|
*status = EXT_ISP_FOCUS_MODE_NORMAL;
|
|
break;
|
|
case AF_MACRO:
|
|
*status = EXT_ISP_FOCUS_MODE_MACRO;
|
|
break;
|
|
case AF_TOUCH:
|
|
*status = EXT_ISP_FOCUS_MODE_TOUCH_AF;
|
|
break;
|
|
case AF_PREVIEW_CAF:
|
|
*status = EXT_ISP_FOCUS_MODE_PREVIEW_CAF;
|
|
break;
|
|
case AF_MOVIE_CAF:
|
|
*status = EXT_ISP_FOCUS_MODE_MOVIE_CAF;
|
|
break;
|
|
case AF_FACE_CAF:
|
|
*status = EXT_ISP_FOCUS_MODE_FACE_CAF;
|
|
break;
|
|
case AF_TOUCH_MACRO:
|
|
*status = EXT_ISP_FOCUS_MODE_TOUCH_MACRO;
|
|
break;
|
|
case AF_TOUCH_CAF:
|
|
*status = EXT_ISP_FOCUS_MODE_TOUCH_CAF;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_flash_mode(struct v4l2_subdev *sd, unsigned int val)
|
|
{
|
|
int ret = 0;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
/* by default force the torch off, value depends on incoming flash mode */
|
|
dev->monitor_params.torch = LED_TORCH_OFF;
|
|
|
|
switch (val) {
|
|
case EXT_ISP_FLASH_MODE_OFF:
|
|
dev->monitor_params.flash_mode = FLASH_MODE_OFF;
|
|
break;
|
|
case EXT_ISP_FLASH_MODE_ON:
|
|
dev->monitor_params.flash_mode = FLASH_MODE_ON;
|
|
break;
|
|
case EXT_ISP_FLASH_MODE_AUTO:
|
|
dev->monitor_params.flash_mode = FLASH_MODE_AUTO;
|
|
break;
|
|
case EXT_ISP_LED_TORCH_OFF:
|
|
dev->monitor_params.flash_mode = LED_TORCH;
|
|
dev->monitor_params.torch = LED_TORCH_OFF;
|
|
break;
|
|
case EXT_ISP_LED_TORCH_ON:
|
|
dev->monitor_params.flash_mode = LED_TORCH;
|
|
dev->monitor_params.torch = LED_TORCH_ON;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Only apply setting if we are in monitor mode */
|
|
if (!is_m10mo_in_monitor_mode(sd))
|
|
return ret;
|
|
|
|
dev_info(&client->dev, "%s: In monitor mode, set flash mode to %d",
|
|
__func__, dev->monitor_params.flash_mode);
|
|
|
|
/* TODO get current flash mode, and apply new setting only when needed? */
|
|
|
|
if (dev->monitor_params.flash_mode == LED_TORCH)
|
|
ret = m10mo_writeb(sd, CATEGORY_LOGLEDFLASH, LED_TORCH,
|
|
dev->monitor_params.torch);
|
|
else
|
|
ret = m10mo_writeb(sd, CATEGORY_LOGLEDFLASH, FLASH_MODE,
|
|
dev->monitor_params.flash_mode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int power_up(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
if (dev->pdata->common.power_ctrl) {
|
|
ret = dev->pdata->common.power_ctrl(sd, 1);
|
|
if (ret)
|
|
goto fail_power_ctrl;
|
|
}
|
|
|
|
if (dev->pdata->common.flisclk_ctrl) {
|
|
ret = dev->pdata->common.flisclk_ctrl(sd, 1);
|
|
if (ret)
|
|
goto fail_clk_off;
|
|
}
|
|
|
|
/**ISP RESET**/
|
|
ret = dev->pdata->common.gpio_ctrl(sd, 1);
|
|
if (ret)
|
|
goto fail_power_off;
|
|
return 0;
|
|
|
|
fail_power_off:
|
|
dev->pdata->common.gpio_ctrl(sd, 0);
|
|
fail_clk_off:
|
|
if (dev->pdata->common.flisclk_ctrl)
|
|
ret = dev->pdata->common.flisclk_ctrl(sd, 0);
|
|
fail_power_ctrl:
|
|
if (dev->pdata->common.power_ctrl)
|
|
ret = dev->pdata->common.power_ctrl(sd, 0);
|
|
return ret;
|
|
}
|
|
|
|
static int power_down(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
ret = dev->pdata->common.gpio_ctrl(sd, 0);
|
|
if (ret)
|
|
dev_err(&client->dev, "gpio failed\n");
|
|
|
|
/* Even if the first one fails we still want to turn clock off */
|
|
if (dev->pdata->common.flisclk_ctrl) {
|
|
ret = dev->pdata->common.flisclk_ctrl(sd, 0);
|
|
if (ret)
|
|
dev_err(&client->dev, "stop clock failed\n");
|
|
}
|
|
|
|
if (dev->pdata->common.power_ctrl) {
|
|
ret = dev->pdata->common.power_ctrl(sd, 0);
|
|
if (ret)
|
|
dev_err(&client->dev, "power off fail\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int __m10mo_bootrom_mode_start(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
u32 dummy;
|
|
int ret;
|
|
|
|
ret = m10mo_wait_mode_change(sd, M10MO_FLASH_WRITE_MODE,
|
|
M10MO_BOOT_TIMEOUT);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev, "Flash rom mode timeout\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Dummy read to verify I2C functionality */
|
|
ret = m10mo_readl(sd, CATEGORY_FLASHROM, REG_FLASH_ADD, &dummy);
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "Dummy I2C access fails\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __m10mo_s_power(struct v4l2_subdev *sd, int on, bool fw_update_mode)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
if (dev->power == on)
|
|
return 0;
|
|
|
|
if (on) {
|
|
dev->mode = M10MO_POWERING_ON;
|
|
m10mo_request_mode_change(sd, M10MO_FLASH_WRITE_MODE);
|
|
|
|
ret = power_up(sd);
|
|
if (ret)
|
|
return ret;
|
|
dev->power = 1;
|
|
|
|
ret = __m10mo_bootrom_mode_start(sd);
|
|
if (ret)
|
|
goto startup_failure;
|
|
|
|
if (!fw_update_mode) {
|
|
ret = __m10mo_fw_start(sd);
|
|
if (ret)
|
|
goto startup_failure;
|
|
}
|
|
} else {
|
|
ret = power_down(sd);
|
|
dev->power = 0;
|
|
}
|
|
|
|
return ret;
|
|
|
|
startup_failure:
|
|
power_down(sd);
|
|
dev->power = 0;
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_set_panorama_monitor(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
u32 val;
|
|
|
|
dev_info(&client->dev,
|
|
"%s mode: %d Width: %d, height: %d, cmd: 0x%x vdis: %d\n",
|
|
__func__, dev->mode, dev->curr_res_table[dev->fmt_idx].width,
|
|
dev->curr_res_table[dev->fmt_idx].height,
|
|
dev->curr_res_table[dev->fmt_idx].command,
|
|
(int)dev->curr_res_table[dev->fmt_idx].vdis);
|
|
|
|
/* Check if m10mo already streaming @ required resolution */
|
|
ret = m10mo_readb(sd, CATEGORY_PARAM, PARAM_MON_SIZE, &val);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* If mode is monitor mode and size same, do not configure again*/
|
|
if (dev->mode == M10MO_MONITOR_MODE_PANORAMA &&
|
|
val == dev->curr_res_table[dev->fmt_idx].command) {
|
|
dev_info(&client->dev,
|
|
"%s Already streaming with required size\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (dev->mode != M10MO_PARAM_SETTING_MODE &&
|
|
dev->mode != M10MO_PARAMETER_MODE) {
|
|
/* Already in panorama mode. So swith to parameter mode */
|
|
ret = __m10mo_param_mode_set(sd);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* Change the Monitor Size */
|
|
ret = m10mo_write(sd, 1, CATEGORY_PARAM, PARAM_MON_SIZE,
|
|
dev->curr_res_table[dev->fmt_idx].command);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Set Panorama mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, CAPTURE_MODE,
|
|
CAP_MODE_PANORAMA);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Setting output to NV12/NV21. */
|
|
ret = m10mo_writeb(sd, CATEGORY_PARAM, OUTPUT_FMT_SELECT,
|
|
OUTPUT_FMT_SELECT_NV12NV21);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Select either NV12 or NV21 based on the format set from user space */
|
|
val = dev->format.code == V4L2_MBUS_FMT_CUSTOM_NV21 ?
|
|
CHOOSE_NV12NV21_FMT_NV21 : CHOOSE_NV12NV21_FMT_NV12;
|
|
ret = m10mo_writeb(sd, CATEGORY_PARAM, CHOOSE_NV12NV21_FMT, val);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Enable metadata (the command sequence PDF-example) */
|
|
ret = m10mo_writeb(sd, CATEGORY_PARAM, MON_METADATA_SUPPORT_CTRL,
|
|
MON_METADATA_SUPPORT_CTRL_EN);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Enable interrupt signal */
|
|
ret = m10mo_writeb(sd, CATEGORY_SYSTEM, SYSTEM_INT_ENABLE, 0x01);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Go to Panorama Monitor mode */
|
|
ret = m10mo_request_mode_change(sd, M10MO_MONITOR_MODE_PANORAMA);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = m10mo_wait_mode_change(sd, M10MO_MONITOR_MODE_PANORAMA,
|
|
M10MO_INIT_TIMEOUT);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
return 0;
|
|
out:
|
|
dev_err(&client->dev, "m10mo_set_panorama_monitor failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_set_zsl_monitor(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int mode = M10MO_GET_RESOLUTION_MODE(dev->fw_type);
|
|
int shot_mode_support = M10MO_SHOT_MODES_SUPPORTED(dev->fw_type);
|
|
const struct m10mo_resolution *capture_res =
|
|
resolutions[mode][M10MO_MODE_CAPTURE_INDEX];
|
|
int ret;
|
|
u32 fmt;
|
|
|
|
dev_info(&client->dev,
|
|
"%s mode: %d width: %d, height: %d, cmd: 0x%x vdis: %d\n",
|
|
__func__, dev->mode, dev->curr_res_table[dev->fmt_idx].width,
|
|
dev->curr_res_table[dev->fmt_idx].height,
|
|
dev->curr_res_table[dev->fmt_idx].command,
|
|
(int)dev->curr_res_table[dev->fmt_idx].vdis);
|
|
|
|
dev_info(&client->dev, "%s capture width: %d, height: %d, cmd: 0x%x\n",
|
|
__func__, capture_res[dev->capture_res_idx].width,
|
|
capture_res[dev->capture_res_idx].height,
|
|
capture_res[dev->capture_res_idx].command);
|
|
|
|
if (dev->mode != M10MO_PARAM_SETTING_MODE &&
|
|
dev->mode != M10MO_PARAMETER_MODE) {
|
|
/*
|
|
* At this stage means we are already at ZSL. So switch to
|
|
* param mode first and reset all the parameters.
|
|
*/
|
|
|
|
ret = __m10mo_param_mode_set(sd);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* Set ZSL mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, CAPTURE_MODE,
|
|
CAP_MODE_INFINITY_ZSL);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Change the Monitor Size */
|
|
ret = m10mo_write(sd, 1, CATEGORY_PARAM, PARAM_MON_SIZE,
|
|
dev->curr_res_table[dev->fmt_idx].command);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Change the capture size */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_PARAM, CAPP_MAIN_IMAGE_SIZE,
|
|
capture_res[dev->capture_res_idx].command);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Set shot mode */
|
|
if (shot_mode_support) {
|
|
ret = m10mo_writeb(sd, CATEGORY_PARAM, SHOT_MODE, dev->shot_mode);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* Select monitor/movie mode */
|
|
ret = m10mo_write(sd, 1, CATEGORY_PARAM, MOVIE_MODE,
|
|
dev->run_mode == CI_MODE_VIDEO ? 0x01 : 0x00);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* vdis on/off */
|
|
m10mo_writeb(sd, CATEGORY_MONITOR, PARAM_VDIS,
|
|
dev->curr_res_table[dev->fmt_idx].vdis ? 0x01 : 0x00);
|
|
|
|
/* Select either NV12 or NV21 based on the format set from user space */
|
|
fmt = dev->format.code == V4L2_MBUS_FMT_CUSTOM_NV21 ?
|
|
CHOOSE_NV12NV21_FMT_NV21 : CHOOSE_NV12NV21_FMT_NV12;
|
|
ret = m10mo_writeb(sd, CATEGORY_PARAM, CHOOSE_NV12NV21_FMT, fmt);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Enable interrupt signal */
|
|
ret = m10mo_writeb(sd, CATEGORY_SYSTEM, SYSTEM_INT_ENABLE, 0x01);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Go to ZSL Monitor mode */
|
|
ret = m10mo_request_mode_change(sd, M10MO_MONITOR_MODE_ZSL);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = m10mo_wait_mode_change(sd, M10MO_MONITOR_MODE_ZSL,
|
|
M10MO_INIT_TIMEOUT);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
return 0;
|
|
out:
|
|
dev_err(&client->dev, "m10mo_set_zsl_monitor failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static u32 __get_dual_capture_value(u8 capture_mode)
|
|
{
|
|
switch(capture_mode) {
|
|
case M10MO_CAPTURE_MODE_ZSL_BURST:
|
|
return DUAL_CAPTURE_BURST_CAPTURE_START;
|
|
case M10MO_CAPTURE_MODE_ZSL_LLS:
|
|
return DUAL_CAPTURE_LLS_CAPTURE_START;
|
|
case M10MO_CAPTURE_MODE_ZSL_NORMAL:
|
|
return DUAL_CAPTURE_ZSL_CAPTURE_START;
|
|
case M10MO_CAPTURE_MODE_ZSL_HDR:
|
|
case M10MO_CAPTURE_MODE_ZSL_RAW:
|
|
default:
|
|
return DUAL_CAPTURE_SINGLE_CAPTURE_START;
|
|
}
|
|
}
|
|
|
|
static int m10mo_set_zsl_capture(struct v4l2_subdev *sd, int sel_frame)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret, i;
|
|
u32 val, dual_status;
|
|
int finish = 0;
|
|
|
|
/* TODO: Fix this. Currently we do not use this */
|
|
(void) sel_frame;
|
|
|
|
val = __get_dual_capture_value(dev->capture_mode);
|
|
|
|
/* Check dual capture status before the capture request */
|
|
for (i = POLL_NUM; i; i--) {
|
|
ret = m10mo_readb(sd, CATEGORY_CAPTURE_CTRL, START_DUAL_STATUS,
|
|
&dual_status);
|
|
if (ret)
|
|
continue;
|
|
|
|
if ((dual_status == 0) || (dual_status == DUAL_STATUS_AF_WORKING)) {
|
|
finish = 1;
|
|
break;
|
|
}
|
|
|
|
msleep(10);
|
|
}
|
|
|
|
/*
|
|
* If last capture not finished yet, return error code
|
|
*/
|
|
if (!finish) {
|
|
dev_err(&client->dev, "%s Device busy. Status check failed %d\n",
|
|
__func__, dual_status);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Start capture, JPEG encode & transfer start */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, START_DUAL_CAPTURE, val);
|
|
dev_dbg(&client->dev, "%s zsl capture trigger result: %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_set_zsl_raw_capture(struct v4l2_subdev *sd)
|
|
{
|
|
int ret;
|
|
|
|
/* Set capture mode - Infinity capture 3 */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, CAPTURE_MODE,
|
|
CAP_MODE_INFINITY_ZSL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Enable interrupt signal */
|
|
ret = m10mo_writeb(sd, CATEGORY_SYSTEM, SYSTEM_INT_ENABLE, 0x01);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Go to ZSL Monitor mode */
|
|
ret = m10mo_request_mode_change(sd, M10MO_MONITOR_MODE_ZSL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_wait_mode_change(sd, M10MO_MONITOR_MODE_ZSL,
|
|
M10MO_INIT_TIMEOUT);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* switch to RAW capture mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, REG_CAP_NV12_MODE,
|
|
RAW_CAPTURE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* RAW mode is set. Now do a normal capture */
|
|
return m10mo_set_zsl_capture(sd, 1);
|
|
}
|
|
|
|
int m10mo_set_burst_mode(struct v4l2_subdev *sd, unsigned int val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret = 0;
|
|
|
|
switch(val) {
|
|
case EXT_ISP_BURST_CAPTURE_CTRL_START:
|
|
/* First check if already in ZSL monitor mode. If not start */
|
|
if (dev->mode != M10MO_MONITOR_MODE_ZSL) {
|
|
ret = m10mo_set_zsl_monitor(sd);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
/* set cap mode to burst so that ZSL cap can differentiate */
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_BURST;
|
|
return m10mo_set_zsl_capture(sd, 1);
|
|
case EXT_ISP_BURST_CAPTURE_CTRL_STOP:
|
|
if (dev->capture_mode != M10MO_CAPTURE_MODE_ZSL_BURST)
|
|
return 0;
|
|
/* Stop the burst capture */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL,
|
|
START_DUAL_CAPTURE, DUAL_CAPTURE_BURST_CAPTURE_STOP);
|
|
if (ret)
|
|
return ret;
|
|
/* captutre mode back to normal */
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_NORMAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_set_lls_mode(struct v4l2_subdev *sd, unsigned int val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
switch(val) {
|
|
case STOP_LLS_MODE:
|
|
/* switch to normal capture. HDR MODE off */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, REG_CAP_NV12_MODE,
|
|
NORMAL_CAPTURE);
|
|
if (!ret)
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_NORMAL;
|
|
break;
|
|
case START_LLS_MODE:
|
|
/* switch to HDR mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, REG_CAP_NV12_MODE,
|
|
LLS_CAPTURE);
|
|
if (!ret)
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_LLS;
|
|
break;
|
|
case RESUME_PREVIEW_IN_LLS_MODE:
|
|
/* switch to HDR mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL,
|
|
START_DUAL_CAPTURE, PREVIEW_IN_NV12_MODE);
|
|
if (!ret)
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_LLS;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_hdr_mode(struct v4l2_subdev *sd, unsigned int val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
switch(val) {
|
|
case STOP_HDR_MODE:
|
|
/* switch to normal capture. HDR MODE off */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, REG_CAP_NV12_MODE,
|
|
NORMAL_CAPTURE);
|
|
if (!ret)
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_NORMAL;
|
|
break;
|
|
case START_HDR_MODE:
|
|
/* switch to HDR mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL, REG_CAP_NV12_MODE,
|
|
HDR_CAPTURE);
|
|
if (!ret)
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_HDR;
|
|
break;
|
|
case RESUME_PREVIEW_IN_HDR_MODE:
|
|
/* switch to HDR mode */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL,
|
|
START_DUAL_CAPTURE, PREVIEW_IN_NV12_MODE);
|
|
if (!ret)
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_HDR;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_shot_mode(struct v4l2_subdev *sd, unsigned int val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int shot_mode_support = M10MO_SHOT_MODES_SUPPORTED(dev->fw_type);
|
|
|
|
if (!shot_mode_support)
|
|
return -EINVAL;
|
|
|
|
switch (val) {
|
|
case EXT_ISP_SHOT_MODE_AUTO:
|
|
dev->shot_mode = SHOT_MODE_AUTO;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_BEAUTY_FACE:
|
|
dev->shot_mode = SHOT_MODE_BEAUTY_FACE;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_BEST_PHOTO:
|
|
dev->shot_mode = SHOT_MODE_BEST_PHOTO;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_DRAMA:
|
|
dev->shot_mode = SHOT_MODE_DRAMA;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_BEST_FACE:
|
|
dev->shot_mode = SHOT_MODE_BEST_FACE;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_ERASER:
|
|
dev->shot_mode = SHOT_MODE_ERASER;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_PANORAMA:
|
|
dev->shot_mode = SHOT_MODE_PANORAMA;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_RICH_TONE_HDR:
|
|
dev->shot_mode = SHOT_MODE_RICH_TONE_HDR;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_NIGHT:
|
|
dev->shot_mode = SHOT_MODE_NIGHT;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_SOUND_SHOT:
|
|
dev->shot_mode = SHOT_MODE_SOUND_SHOT;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_ANIMATED_PHOTO:
|
|
dev->shot_mode = SHOT_MODE_ANIMATED_PHOTO;
|
|
break;
|
|
case EXT_ISP_SHOT_MODE_SPORTS:
|
|
dev->shot_mode = SHOT_MODE_SPORTS;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_s_power(struct v4l2_subdev *sd, int on)
|
|
{
|
|
int ret;
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
ret = __m10mo_s_power(sd, on, false);
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_single_capture_process(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
u32 fmt;
|
|
|
|
/* Select frame */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL,
|
|
CAPC_SEL_FRAME_MAIN, 0x01);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Image format */
|
|
if (dev->format.code == V4L2_MBUS_FMT_JPEG_1X8)
|
|
fmt = CAPTURE_FORMAT_JPEG8;
|
|
else
|
|
fmt = CAPTURE_FORMAT_YUV422;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_PARAM, CAPP_YUVOUT_MAIN, fmt);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Image size */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_PARAM, CAPP_MAIN_IMAGE_SIZE,
|
|
dev->curr_res_table[dev->fmt_idx].command);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Start image transfer */
|
|
ret = m10mo_writeb(sd, CATEGORY_CAPTURE_CTRL,
|
|
CAPC_TRANSFER_START, 0x01);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t m10mo_irq_thread(int irq, void *dev_id)
|
|
{
|
|
struct v4l2_subdev *sd = (struct v4l2_subdev *)dev_id;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
u32 int_factor;
|
|
|
|
/* Clear interrupt by reading interrupt factor register */
|
|
(void) m10mo_read(sd, 1, CATEGORY_SYSTEM, SYSTEM_INT_FACTOR,
|
|
&int_factor);
|
|
|
|
dev_info(&client->dev, "INT_FACTOR: 0x%x\n", int_factor);
|
|
|
|
switch (dev->requested_mode) {
|
|
case M10MO_FLASH_WRITE_MODE:
|
|
if (dev->mode == M10MO_POWERING_ON)
|
|
dev->mode = M10MO_FLASH_WRITE_MODE;
|
|
else
|
|
dev_err(&client->dev, "Illegal flash write mode request\n");
|
|
break;
|
|
case M10MO_PARAM_SETTING_MODE:
|
|
if (int_factor & REG_INT_STATUS_MODE) {
|
|
dev->mode = M10MO_PARAM_SETTING_MODE;
|
|
}
|
|
break;
|
|
case M10MO_PARAMETER_MODE:
|
|
if (int_factor & REG_INT_STATUS_MODE) {
|
|
dev->mode = M10MO_PARAMETER_MODE;
|
|
}
|
|
break;
|
|
case M10MO_MONITOR_MODE:
|
|
if (int_factor & REG_INT_STATUS_MODE) {
|
|
dev->mode = M10MO_MONITOR_MODE;
|
|
}
|
|
break;
|
|
case M10MO_MONITOR_MODE_ZSL:
|
|
if (int_factor & REG_INT_STATUS_MODE) {
|
|
dev->mode = M10MO_MONITOR_MODE_ZSL;
|
|
}
|
|
break;
|
|
case M10MO_MONITOR_MODE_PANORAMA:
|
|
if (int_factor & REG_INT_STATUS_MODE) {
|
|
dev->mode = M10MO_MONITOR_MODE_PANORAMA;
|
|
}
|
|
break;
|
|
case M10MO_SINGLE_CAPTURE_MODE:
|
|
if (int_factor & REG_INT_STATUS_CAPTURE) {
|
|
dev->mode = M10MO_SINGLE_CAPTURE_MODE;
|
|
dev->fw_ops->single_capture_process(sd);
|
|
}
|
|
break;
|
|
case M10MO_BURST_CAPTURE_MODE:
|
|
if (int_factor & REG_INT_STATUS_MODE)
|
|
dev->mode = M10MO_BURST_CAPTURE_MODE;
|
|
break;
|
|
case M10MO_MONITOR_MODE_HIGH_SPEED:
|
|
if (int_factor & REG_INT_STATUS_MODE) {
|
|
dev->mode = M10MO_MONITOR_MODE_HIGH_SPEED;
|
|
}
|
|
break;
|
|
default:
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (dev->requested_mode == dev->mode)
|
|
dev->requested_mode = M10MO_NO_MODE_REQUEST;
|
|
|
|
wake_up_interruptible(&dev->irq_waitq);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int m10mo_setup_irq(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int pin, ret;
|
|
|
|
if (!dev->pdata->common.gpio_intr_ctrl) {
|
|
dev_err(&client->dev,
|
|
"Missing gpio information in interrupt setup!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pin = dev->pdata->common.gpio_intr_ctrl(sd);
|
|
if (pin < 0) {
|
|
ret = pin;
|
|
goto out;
|
|
}
|
|
|
|
ret = gpio_to_irq(pin);
|
|
|
|
if (ret < 0) {
|
|
dev_err(&client->dev, "Configure gpio to irq failed!\n");
|
|
goto out;
|
|
}
|
|
client->irq = ret;
|
|
|
|
ret = request_threaded_irq(client->irq, NULL, m10mo_irq_thread,
|
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT, M10MO_NAME, sd);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev, "Cannot register IRQ: %d\n", ret);
|
|
goto out;
|
|
}
|
|
return 0;
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int get_resolution_index(const struct m10mo_resolution *res,
|
|
int entries, int w, int h)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < entries; i++) {
|
|
if (w != res[i].width)
|
|
continue;
|
|
if (h != res[i].height)
|
|
continue;
|
|
/* Found it */
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int __m10mo_try_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt, bool update_fmt)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct atomisp_input_stream_info *stream_info =
|
|
(struct atomisp_input_stream_info *)fmt->reserved;
|
|
const struct m10mo_resolution *res;
|
|
int entries, idx;
|
|
int mode = M10MO_GET_RESOLUTION_MODE(dev->fw_type);
|
|
|
|
if (fmt->code != V4L2_MBUS_FMT_JPEG_1X8 &&
|
|
fmt->code != V4L2_MBUS_FMT_UYVY8_1X16 &&
|
|
fmt->code != V4L2_MBUS_FMT_CUSTOM_NV12 &&
|
|
fmt->code != V4L2_MBUS_FMT_CUSTOM_NV21 &&
|
|
fmt->code != V4L2_MBUS_FMT_CUSTOM_M10MO_RAW) {
|
|
dev_info(&client->dev,
|
|
"%s unsupported code: 0x%x. Set to NV12\n",
|
|
__func__, fmt->code);
|
|
fmt->code = V4L2_MBUS_FMT_CUSTOM_NV12;
|
|
}
|
|
|
|
/* In ZSL case, capture table needs to be handled separately */
|
|
if (stream_info->stream == ATOMISP_INPUT_STREAM_CAPTURE &&
|
|
(dev->run_mode == CI_MODE_PREVIEW ||
|
|
dev->run_mode == CI_MODE_VIDEO ||
|
|
dev->run_mode == CI_MODE_CONTINUOUS)) {
|
|
res = resolutions[mode][M10MO_MODE_CAPTURE_INDEX];
|
|
entries =
|
|
resolutions_sizes[mode][M10MO_MODE_CAPTURE_INDEX];
|
|
} else {
|
|
res = dev->curr_res_table;
|
|
entries = dev->entries_curr_table;
|
|
}
|
|
|
|
/* check if the given resolutions are spported */
|
|
idx = get_resolution_index(res, entries, fmt->width, fmt->height);
|
|
if (idx < 0) {
|
|
dev_err(&client->dev, "%s unsupported resolution: %dx%d\n",
|
|
__func__, fmt->width, fmt->height);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* If the caller wants to get updated fmt values based on the search */
|
|
if (update_fmt) {
|
|
if (fmt->code == V4L2_MBUS_FMT_JPEG_1X8) {
|
|
fmt->width = dev->mipi_params.jpeg_width;
|
|
fmt->height = dev->mipi_params.jpeg_height;
|
|
} else if (fmt->code == V4L2_MBUS_FMT_CUSTOM_M10MO_RAW) {
|
|
fmt->width = dev->mipi_params.raw_width;
|
|
fmt->height = dev->mipi_params.raw_height;
|
|
} else {
|
|
fmt->width = res[idx].width;
|
|
fmt->height = res[idx].height;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
static int m10mo_try_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int idx;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
idx = dev->fw_ops->try_mbus_fmt(sd, fmt, true);
|
|
mutex_unlock(&dev->input_lock);
|
|
return idx >= 0 ? 0 : -EINVAL;
|
|
}
|
|
|
|
static int m10mo_get_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
fmt->width = dev->curr_res_table[dev->fmt_idx].width;
|
|
fmt->height = dev->curr_res_table[dev->fmt_idx].height;
|
|
fmt->code = dev->format.code;
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __m10mo_update_stream_info(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct atomisp_input_stream_info *stream_info =
|
|
(struct atomisp_input_stream_info*)fmt->reserved;
|
|
int mode = M10MO_GET_RESOLUTION_MODE(dev->fw_type);
|
|
|
|
/* TODO: Define a FW Type as well. Resolution could be reused */
|
|
switch (mode) {
|
|
case M10MO_RESOLUTION_MODE_0:
|
|
/* TODO: handle FW type cases here */
|
|
break;
|
|
case M10MO_RESOLUTION_MODE_1:
|
|
/* Raw Capture is a special case. Capture data comes like HDR */
|
|
if (fmt->code == V4L2_MBUS_FMT_JPEG_1X8 ||
|
|
fmt->code == V4L2_MBUS_FMT_CUSTOM_M10MO_RAW) {
|
|
/* fill stream info */
|
|
stream_info->ch_id = M10MO_ZSL_JPEG_VIRTUAL_CHANNEL;
|
|
stream_info->isys_configs = 1;
|
|
stream_info->isys_info[0].input_format =
|
|
(u8)ATOMISP_INPUT_FORMAT_USER_DEF3;
|
|
stream_info->isys_info[0].width = 0;
|
|
stream_info->isys_info[0].height = 0;
|
|
} else if (fmt->code == V4L2_MBUS_FMT_CUSTOM_NV12 ||
|
|
fmt->code == V4L2_MBUS_FMT_CUSTOM_NV21) {
|
|
stream_info->ch_id = M10MO_ZSL_NV12_VIRTUAL_CHANNEL;
|
|
stream_info->isys_configs = 2;
|
|
/* first stream */
|
|
stream_info->isys_info[0].input_format =
|
|
(u8)ATOMISP_INPUT_FORMAT_USER_DEF1;
|
|
stream_info->isys_info[0].width = (u16)fmt->width;
|
|
stream_info->isys_info[0].height = (u16)fmt->height;
|
|
/* Second stream */
|
|
stream_info->isys_info[1].input_format =
|
|
(u8)ATOMISP_INPUT_FORMAT_USER_DEF2;
|
|
stream_info->isys_info[1].width = (u16)fmt->width;
|
|
stream_info->isys_info[1].height = (u16)fmt->height / 2;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int __m10mo_set_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct atomisp_input_stream_info *stream_info =
|
|
(struct atomisp_input_stream_info *)fmt->reserved;
|
|
int mode = M10MO_GET_RESOLUTION_MODE(dev->fw_type);
|
|
int index;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
index = dev->fw_ops->try_mbus_fmt(sd, fmt, false);
|
|
if (index < 0) {
|
|
mutex_unlock(&dev->input_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev->format.code = fmt->code;
|
|
dev->fmt_idx = index;
|
|
if (stream_info->stream == ATOMISP_INPUT_STREAM_CAPTURE) {
|
|
/* Save the index for selecting the capture resolution */
|
|
dev->capture_res_idx = dev->fmt_idx;
|
|
}
|
|
|
|
/*
|
|
* In ZSL Capture cases, for capture an image the run mode is not
|
|
* changed. So we need to maintain a separate cpature table index
|
|
* to select the snapshot sizes.
|
|
*/
|
|
if (stream_info->stream == ATOMISP_INPUT_STREAM_CAPTURE &&
|
|
(dev->run_mode == CI_MODE_PREVIEW ||
|
|
dev->run_mode == CI_MODE_VIDEO ||
|
|
dev->run_mode == CI_MODE_CONTINUOUS))
|
|
dev->capture_res_idx = dev->fmt_idx;
|
|
|
|
dev_dbg(&client->dev,
|
|
"%s index prev/cap: %d/%d width: %d, height: %d, code; 0x%x\n",
|
|
__func__, dev->fmt_idx, dev->capture_res_idx, fmt->width,
|
|
fmt->height, dev->format.code);
|
|
|
|
/* Make the fixed width and height for JPEG and RAW formats */
|
|
if (dev->format.code == V4L2_MBUS_FMT_JPEG_1X8) {
|
|
/* The m10mo can only run JPEG in 30fps or lower */
|
|
dev->fps = M10MO_NORMAL_FPS;
|
|
fmt->width = dev->mipi_params.jpeg_width;
|
|
fmt->height = dev->mipi_params.jpeg_height;
|
|
} else if (dev->format.code == V4L2_MBUS_FMT_CUSTOM_M10MO_RAW) {
|
|
fmt->width = dev->mipi_params.raw_width;
|
|
fmt->height = dev->mipi_params.raw_height;
|
|
}
|
|
|
|
/* Update the stream info. Atomisp uses this for configuring mipi */
|
|
__m10mo_update_stream_info(sd, fmt);
|
|
|
|
/*
|
|
* Handle raw capture mode separately. Update the capture mode to RAW
|
|
* capture now. So that the next streamon call will start RAW capture.
|
|
*/
|
|
if (mode == M10MO_RESOLUTION_MODE_1 &&
|
|
dev->format.code == V4L2_MBUS_FMT_CUSTOM_M10MO_RAW) {
|
|
dev_dbg(&client->dev, "%s RAW capture mode\n", __func__);
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_RAW;
|
|
dev->capture_res_idx = dev->fmt_idx;
|
|
dev->fmt_idx = 0;
|
|
}
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_set_mbus_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
return dev->fw_ops->set_mbus_fmt(sd, fmt);
|
|
}
|
|
|
|
static int m10mo_identify_fw_type(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct m10mo_fw_id *fw_ids = NULL;
|
|
char buffer[M10MO_MAX_FW_ID_STRING];
|
|
int ret;
|
|
|
|
int m10mo_fw_address_cnt = m10mo_get_fw_address_count();
|
|
int i;
|
|
|
|
ret = dev->pdata->identify_fw();
|
|
if (ret != -1) {
|
|
dev->fw_type = ret;
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < m10mo_fw_address_cnt; i++) {
|
|
fw_ids = dev->pdata->fw_ids;
|
|
if (!fw_ids)
|
|
return 0;
|
|
|
|
ret = m10mo_get_isp_fw_version_string(dev, buffer,
|
|
sizeof(buffer), i);
|
|
if (ret)
|
|
return ret;
|
|
|
|
while (fw_ids->id_string) {
|
|
if (!strncmp(fw_ids->id_string, buffer,
|
|
strlen(fw_ids->id_string)))
|
|
{
|
|
dev_info(&client->dev, "FW id %s detected\n", buffer);
|
|
dev->fw_type = fw_ids->fw_type;
|
|
dev->fw_addr_id = i;
|
|
return 0;
|
|
}
|
|
fw_ids++;
|
|
}
|
|
}
|
|
|
|
dev_err(&client->dev, "FW id string table given but no match found");
|
|
return 0;
|
|
}
|
|
|
|
static void m10mo_mipi_initialization(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int id = M10MO_GET_MIPI_PACKET_SIZE_IDX(dev->fw_type);
|
|
u32 mipi_packet_size = dev->pdata->mipi_packet_size[id];
|
|
|
|
dev->mipi_params.jpeg_width = mipi_packet_size;
|
|
dev->mipi_params.jpeg_height = M10MO_MAX_YUV422_SIZE / mipi_packet_size;
|
|
|
|
dev->mipi_params.raw_width = mipi_packet_size;
|
|
dev->mipi_params.raw_height = M10MO_MAX_RAW_SIZE / mipi_packet_size;
|
|
|
|
dev_dbg(&client->dev,
|
|
"%s JPEG: W=%d H=%d, RAW: W=%d H=%d\n", __func__,
|
|
dev->mipi_params.jpeg_width, dev->mipi_params.jpeg_height,
|
|
dev->mipi_params.raw_width, dev->mipi_params.raw_height);
|
|
}
|
|
|
|
int m10mo_set_still_capture(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
|
|
dev_info(&client->dev, "%s mode: %d width: %d, height: %d, cmd: 0x%x\n",
|
|
__func__, dev->mode, dev->curr_res_table[dev->fmt_idx].width,
|
|
dev->curr_res_table[dev->fmt_idx].height,
|
|
dev->curr_res_table[dev->fmt_idx].command);
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_SYSTEM, SYSTEM_INT_ENABLE, 0x08);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Set capture mode */
|
|
ret = m10mo_request_mode_change(sd, M10MO_SINGLE_CAPTURE_MODE);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_run_mode(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
/*
|
|
* Handle RAW capture mode separately irrespective of the run mode
|
|
* being configured. Start the RAW capture right away.
|
|
*/
|
|
if (dev->capture_mode == M10MO_CAPTURE_MODE_ZSL_RAW) {
|
|
/*
|
|
* As RAW capture is done from a command line tool, we are not
|
|
* restarting the preview after the RAW capture. So it is ok
|
|
* to reset the RAW capture mode here because the next RAW
|
|
* capture has to start from the Set format onwards.
|
|
*/
|
|
dev->capture_mode = M10MO_CAPTURE_MODE_ZSL_NORMAL;
|
|
return m10mo_set_zsl_raw_capture(sd);
|
|
}
|
|
|
|
switch (dev->run_mode) {
|
|
case CI_MODE_STILL_CAPTURE:
|
|
ret = m10mo_set_still_capture(sd);
|
|
break;
|
|
default:
|
|
/* TODO: Revisit this logic on switching to panorama */
|
|
if (dev->curr_res_table[dev->fmt_idx].command == 0x43)
|
|
ret = m10mo_set_panorama_monitor(sd);
|
|
else
|
|
ret = m10mo_set_zsl_monitor(sd);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int m10mo_streamoff(struct v4l2_subdev *sd)
|
|
{
|
|
return __m10mo_param_mode_set(sd);
|
|
}
|
|
|
|
int m10mo_test_pattern(struct v4l2_subdev *sd, u8 val)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct m10mo_fw_ops fw_ops = {
|
|
.set_run_mode = m10mo_set_run_mode,
|
|
.set_burst_mode = m10mo_set_burst_mode,
|
|
.stream_off = m10mo_streamoff,
|
|
.single_capture_process = m10mo_single_capture_process,
|
|
.try_mbus_fmt = __m10mo_try_mbus_fmt,
|
|
.set_mbus_fmt = __m10mo_set_mbus_fmt,
|
|
.test_pattern = m10mo_test_pattern,
|
|
};
|
|
|
|
void m10mo_handlers_init(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
switch (dev->fw_type) {
|
|
case M10MO_FW_TYPE_1:
|
|
dev->fw_ops = &fw_type1_5_ops;
|
|
break;
|
|
case M10MO_FW_TYPE_2:
|
|
dev->fw_ops = &fw_type2_ops;
|
|
break;
|
|
case M10MO_FW_TYPE_5:
|
|
dev->fw_ops = &fw_type1_5_ops;
|
|
break;
|
|
default:
|
|
dev->fw_ops = &fw_ops;
|
|
}
|
|
}
|
|
|
|
static int m10mo_s_config(struct v4l2_subdev *sd, int irq)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret;
|
|
u16 result = M10MO_INVALID_CHECKSUM;
|
|
int id = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
init_waitqueue_head(&dev->irq_waitq);
|
|
|
|
dev->fw_type = dev->pdata->def_fw_type;
|
|
dev->ref_clock = dev->pdata->ref_clock_rate[id];
|
|
|
|
if (dev->pdata->common.platform_init) {
|
|
ret = dev->pdata->common.platform_init(client);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* set up irq */
|
|
ret = m10mo_setup_irq(sd);
|
|
if (ret) {
|
|
dev_err(&client->dev, "IRQ err.\n");
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
ret = __m10mo_s_power(sd, 1, true);
|
|
if (ret) {
|
|
dev_err(&client->dev, "power-up err.\n");
|
|
goto free_irq;
|
|
}
|
|
|
|
if (dev->pdata->common.csi_cfg) {
|
|
ret = dev->pdata->common.csi_cfg(sd, 1);
|
|
if (ret)
|
|
goto fail;
|
|
}
|
|
|
|
ret = m10mo_fw_checksum(dev, &result);
|
|
if (ret) {
|
|
dev_err(&client->dev, "Checksum calculation fails.\n");
|
|
goto fail;
|
|
}
|
|
if (result != M10MO_VALID_CHECKSUM)
|
|
dev_err(&client->dev, "Firmware checksum is not 0.\n");
|
|
/* TBD: Trig FW update here */
|
|
|
|
/*
|
|
* We don't care about the return value here. Even in case of
|
|
* wrong or non-existent fw this phase must pass.
|
|
* FW can be updated later.
|
|
*/
|
|
m10mo_identify_fw_type(sd);
|
|
|
|
/*
|
|
* Only after identify_fw_type the correct dev->fw_type
|
|
* can be got, so here update the ref_clock
|
|
*/
|
|
id = M10MO_GET_CLOCK_RATE_MODE(dev->fw_type);
|
|
dev->ref_clock = dev->pdata->ref_clock_rate[id];
|
|
|
|
m10mo_mipi_initialization(sd);
|
|
|
|
/* Set proper function pointers based on FW_TYPE */
|
|
m10mo_handlers_init(sd);
|
|
|
|
ret = __m10mo_s_power(sd, 0, true);
|
|
if (ret) {
|
|
dev_err(&client->dev, "power-down err.\n");
|
|
goto free_irq;
|
|
}
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return 0;
|
|
|
|
fail:
|
|
__m10mo_s_power(sd, 0, true);
|
|
free_irq:
|
|
free_irq(client->irq, sd);
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
dev_err(&client->dev, "External ISP power-gating failed\n");
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int m10mo_recovery(struct v4l2_subdev *sd)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int ret = 0;
|
|
|
|
/* still power off sensor in case power is cut off abnormally*/
|
|
ret = __m10mo_s_power(sd, 0, false);
|
|
if (ret) {
|
|
dev_err(&client->dev, "power-down err.\n");
|
|
return ret;
|
|
}
|
|
usleep_range(100, 200);
|
|
|
|
ret = __m10mo_s_power(sd, 1, false);
|
|
if (ret) {
|
|
dev_err(&client->dev, "power-up err.\n");
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_s_stream(struct v4l2_subdev *sd, int enable)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (enable) {
|
|
ret = dev->fw_ops->set_run_mode(sd);
|
|
if (ret) {
|
|
ret = m10mo_recovery(sd);
|
|
if (ret) {
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
ret = dev->fw_ops->set_run_mode(sd);
|
|
}
|
|
} else {
|
|
ret = dev->fw_ops->stream_off(sd);
|
|
}
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
/*
|
|
* Dump M10MO log when stream-off with checking folder
|
|
*/
|
|
if (!enable)
|
|
m10mo_dump_log(sd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
m10mo_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
if (fsize->index >= dev->entries_curr_table)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
|
|
fsize->discrete.width = dev->curr_res_table[fsize->index].width;
|
|
fsize->discrete.height = dev->curr_res_table[fsize->index].height;
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_enum_mbus_code(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
if (code->index)
|
|
return -EINVAL;
|
|
|
|
code->code = dev->format.code;
|
|
return 0;
|
|
}
|
|
|
|
static struct v4l2_mbus_framefmt *
|
|
__m10mo_get_pad_format(struct m10mo_device *sensor,
|
|
struct v4l2_subdev_fh *fh, unsigned int pad,
|
|
enum v4l2_subdev_format_whence which)
|
|
{
|
|
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
|
|
m10mo_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct m10mo_device *snr = to_m10mo_sensor(sd);
|
|
struct v4l2_mbus_framefmt *format =
|
|
__m10mo_get_pad_format(snr, fh, fmt->pad, fmt->which);
|
|
|
|
if (format == NULL)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&snr->input_lock);
|
|
fmt->format = *format;
|
|
mutex_unlock(&snr->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
m10mo_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct m10mo_device *snr = to_m10mo_sensor(sd);
|
|
struct v4l2_mbus_framefmt *format =
|
|
__m10mo_get_pad_format(snr, fh, fmt->pad, fmt->which);
|
|
|
|
if (format == NULL)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&snr->input_lock);
|
|
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
|
snr->format = fmt->format;
|
|
mutex_unlock(&snr->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct media_entity_operations m10mo_entity_ops = {
|
|
.link_setup = NULL,
|
|
};
|
|
|
|
static int m10mo_set_flicker_freq(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
unsigned int flicker_freq;
|
|
|
|
switch (val) {
|
|
case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
|
|
flicker_freq = M10MO_FLICKER_OFF;
|
|
break;
|
|
case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
|
|
flicker_freq = M10MO_FLICKER_50HZ;
|
|
break;
|
|
case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
|
|
flicker_freq = M10MO_FLICKER_60HZ;
|
|
break;
|
|
case V4L2_CID_POWER_LINE_FREQUENCY_AUTO:
|
|
flicker_freq = M10MO_FLICKER_AUTO;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return m10mo_writeb(sd, CATEGORY_AE, AE_FLICKER, flicker_freq);
|
|
}
|
|
|
|
static int m10mo_set_metering(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
unsigned int metering;
|
|
|
|
switch (val) {
|
|
case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:
|
|
metering = M10MO_METERING_CENTER;
|
|
break;
|
|
case V4L2_EXPOSURE_METERING_SPOT:
|
|
metering = M10MO_METERING_SPOT;
|
|
break;
|
|
case V4L2_EXPOSURE_METERING_AVERAGE:
|
|
metering = M10MO_METERING_AVERAGE;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return m10mo_writeb(sd, CATEGORY_AE, AE_MODE, metering);
|
|
}
|
|
|
|
static const unsigned short wb_lut[][2] = {
|
|
{ V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT },
|
|
{ V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_L },
|
|
{ V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_H },
|
|
{ V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON },
|
|
{ V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT },
|
|
{ V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT },
|
|
{ V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY },
|
|
{ V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE },
|
|
{ V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO },
|
|
};
|
|
|
|
static int m10mo_set_white_balance(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
int i, ret;
|
|
int awb = REG_AWB_MANUAL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(wb_lut); i++) {
|
|
if (val == wb_lut[i][0])
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(wb_lut))
|
|
return -EINVAL;
|
|
|
|
if (wb_lut[i][0] == V4L2_WHITE_BALANCE_AUTO)
|
|
awb = REG_AWB_AUTO;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_WB,
|
|
AWB_MODE, awb);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (awb == REG_AWB_MANUAL)
|
|
ret = m10mo_writeb(sd, CATEGORY_WB,
|
|
AWB_PRESET, wb_lut[i][1]);
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_ev_bias(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
/* 0x04 refers to 0.0EV value in m10mo HW */
|
|
/* val refers to EV units, where the */
|
|
/* value 1000 stands for +1EV */
|
|
|
|
int ev_bias = 0x04 + (val/M10MO_EV_STEP);
|
|
return m10mo_writeb(sd, CATEGORY_AE, AE_EV_BIAS, ev_bias);
|
|
}
|
|
|
|
static int m10mo_get_ev_bias(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
int ret;
|
|
u32 ev_bias;
|
|
|
|
ret = m10mo_readb(sd, CATEGORY_AE, AE_EV_BIAS, &ev_bias);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*val = (ev_bias-4) * M10MO_EV_STEP;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const unsigned short iso_lut[][2] = {
|
|
{ 100, REG_AE_ISOMODE_ISO100},
|
|
{ 200, REG_AE_ISOMODE_ISO200},
|
|
{ 400, REG_AE_ISOMODE_ISO400},
|
|
{ 800, REG_AE_ISOMODE_ISO800},
|
|
{ 1600, REG_AE_ISOMODE_ISO1600},
|
|
};
|
|
|
|
static int m10mo_set_iso_sensitivity(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret, i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(iso_lut); i++) {
|
|
if (val == iso_lut[i][0])
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(iso_lut))
|
|
return -EINVAL;
|
|
|
|
if (dev->iso_mode == V4L2_ISO_SENSITIVITY_MANUAL) {
|
|
ret = m10mo_writeb(sd, CATEGORY_AE,
|
|
AE_ISOMODE, iso_lut[i][1]);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
dev->iso_sensitivity = iso_lut[i][1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_set_iso_mode(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
|
|
if (val == V4L2_ISO_SENSITIVITY_AUTO) {
|
|
ret = m10mo_writeb(sd, CATEGORY_AE,
|
|
AE_ISOMODE, REG_AE_ISOMODE_AUTO);
|
|
if (ret < 0)
|
|
return ret;
|
|
dev->iso_mode = V4L2_ISO_SENSITIVITY_AUTO;
|
|
} else {
|
|
ret = m10mo_writeb(sd, CATEGORY_AE,
|
|
AE_ISOMODE, dev->iso_sensitivity);
|
|
if (ret < 0)
|
|
return ret;
|
|
dev->iso_mode = V4L2_ISO_SENSITIVITY_MANUAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const unsigned short ce_lut[][2] = {
|
|
{ V4L2_COLORFX_NONE, COLOR_EFFECT_NONE},
|
|
{ V4L2_COLORFX_NEGATIVE, COLOR_EFFECT_NEGATIVE},
|
|
{ V4L2_COLORFX_WARM, COLOR_EFFECT_WARM},
|
|
{ V4L2_COLORFX_COLD, COLOR_EFFECT_COLD},
|
|
{ V4L2_COLORFX_WASHED, COLOR_EFFECT_WASHED},
|
|
};
|
|
|
|
static const unsigned short cbcr_lut[][3] = {
|
|
{ V4L2_COLORFX_SEPIA, COLOR_CFIXB_SEPIA, COLOR_CFIXR_SEPIA},
|
|
{ V4L2_COLORFX_BW, COLOR_CFIXB_BW, COLOR_CFIXR_BW},
|
|
{ V4L2_COLORFX_RED, COLOR_CFIXB_RED, COLOR_CFIXR_RED},
|
|
{ V4L2_COLORFX_GREEN, COLOR_CFIXB_GREEN, COLOR_CFIXR_GREEN},
|
|
{ V4L2_COLORFX_BLUE, COLOR_CFIXB_BLUE, COLOR_CFIXR_BLUE},
|
|
{ V4L2_COLORFX_PINK , COLOR_CFIXB_PINK, COLOR_CFIXR_PINK},
|
|
{ V4L2_COLORFX_YELLOW, COLOR_CFIXB_YELLOW, COLOR_CFIXR_YELLOW},
|
|
{ V4L2_COLORFX_PURPLE, COLOR_CFIXB_PURPLE, COLOR_CFIXR_PURPLE},
|
|
{ V4L2_COLORFX_ANTIQUE, COLOR_CFIXB_ANTIQUE, COLOR_CFIXR_ANTIQUE},
|
|
};
|
|
|
|
static int m10mo_set_cb_cr(struct v4l2_subdev *sd, u8 cfixb, u8 cfixr)
|
|
{
|
|
int ret;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_MONITOR,
|
|
MONITOR_CFIXB, cfixb);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_MONITOR,
|
|
MONITOR_CFIXR, cfixr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_MONITOR,
|
|
MONITOR_COLOR_EFFECT, COLOR_EFFECT_ON);
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_color_effect(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
int i, ret;
|
|
|
|
switch (val) {
|
|
case V4L2_COLORFX_NONE:
|
|
case V4L2_COLORFX_NEGATIVE:
|
|
case V4L2_COLORFX_WARM:
|
|
case V4L2_COLORFX_COLD:
|
|
case V4L2_COLORFX_WASHED:
|
|
for (i = 0; i < ARRAY_SIZE(ce_lut); i++) {
|
|
if (val == ce_lut[i][0])
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(ce_lut))
|
|
return -EINVAL;
|
|
|
|
ret = m10mo_writeb(sd, CATEGORY_MONITOR,
|
|
MONITOR_COLOR_EFFECT, ce_lut[i][1]);
|
|
break;
|
|
case V4L2_COLORFX_SEPIA:
|
|
case V4L2_COLORFX_BW:
|
|
case V4L2_COLORFX_ANTIQUE:
|
|
case V4L2_COLORFX_RED:
|
|
case V4L2_COLORFX_GREEN:
|
|
case V4L2_COLORFX_BLUE:
|
|
case V4L2_COLORFX_PINK:
|
|
case V4L2_COLORFX_YELLOW:
|
|
case V4L2_COLORFX_PURPLE:
|
|
for (i = 0; i < ARRAY_SIZE(cbcr_lut); i++) {
|
|
if (val == cbcr_lut[i][0])
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(cbcr_lut))
|
|
return -EINVAL;
|
|
|
|
ret = m10mo_set_cb_cr(sd, cbcr_lut[i][1], cbcr_lut[i][2]);
|
|
break;
|
|
case V4L2_COLORFX_SET_CBCR:
|
|
ret = m10mo_set_cb_cr(sd, dev->colorfx_cb, dev->colorfx_cr);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_set_color_effect_cbcr(struct v4l2_subdev *sd, s32 val)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int ret;
|
|
u8 cr, cb;
|
|
|
|
cr = val & 0xff;
|
|
cb = (val >> 8) & 0xff;
|
|
|
|
if (dev->colorfx->cur.val == V4L2_COLORFX_SET_CBCR) {
|
|
ret = m10mo_set_cb_cr(sd, cb, cr);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
dev->colorfx_cr = cr;
|
|
dev->colorfx_cb = cb;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_get_focal(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
*val = (M10MO_FOCAL_LENGTH_NUM << 16) | M10MO_FOCAL_LENGTH_DEM;
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_get_fnumber(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
*val = (M10MO_F_NUMBER_DEFAULT_NUM << 16) | M10MO_F_NUMBER_DEM;
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_get_fnumber_range(struct v4l2_subdev *sd, s32 *val)
|
|
{
|
|
*val = (M10MO_F_NUMBER_DEFAULT_NUM << 24) |
|
|
(M10MO_F_NUMBER_DEM << 16) |
|
|
(M10MO_F_NUMBER_DEFAULT_NUM << 8) | M10MO_F_NUMBER_DEM;
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct m10mo_device *dev = container_of(
|
|
ctrl->handler, struct m10mo_device, ctrl_handler);
|
|
int ret = 0;
|
|
|
|
if (!dev->power)
|
|
return 0;
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_POWER_LINE_FREQUENCY:
|
|
ret = m10mo_set_flicker_freq(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_EXPOSURE_METERING:
|
|
ret = m10mo_set_metering(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_START_ZSL_CAPTURE:
|
|
if (ctrl->val)
|
|
ret = m10mo_set_zsl_capture(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
|
|
ret = m10mo_set_white_balance(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_EXPOSURE:
|
|
ret = m10mo_set_ev_bias(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_ISO_SENSITIVITY:
|
|
ret = m10mo_set_iso_sensitivity(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_ISO_SENSITIVITY_AUTO:
|
|
ret = m10mo_set_iso_mode(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_COLORFX:
|
|
ret = m10mo_set_color_effect(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_COLORFX_CBCR:
|
|
ret = m10mo_set_color_effect_cbcr(&dev->sd, ctrl->val);
|
|
break;
|
|
case V4L2_CID_TEST_PATTERN:
|
|
ret = dev->fw_ops->test_pattern(&dev->sd, ctrl->val);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const u32 m10mo_mipi_freq[] = {
|
|
M10MO_MIPI_FREQ_0,
|
|
M10MO_MIPI_FREQ_1,
|
|
};
|
|
|
|
static int m10mo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct m10mo_device *dev = container_of(
|
|
ctrl->handler, struct m10mo_device, ctrl_handler);
|
|
int ret = 0;
|
|
int id = M10MO_GET_MIPI_FREQ_MODE(dev->fw_type);
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_LINK_FREQ:
|
|
ctrl->val = m10mo_mipi_freq[id];
|
|
break;
|
|
case V4L2_CID_EXPOSURE:
|
|
ret = m10mo_get_ev_bias(&dev->sd, &ctrl->val);
|
|
break;
|
|
case V4L2_CID_FOCAL_ABSOLUTE:
|
|
ret = m10mo_get_focal(&dev->sd, &ctrl->val);
|
|
break;
|
|
case V4L2_CID_FNUMBER_ABSOLUTE:
|
|
ret = m10mo_get_fnumber(&dev->sd, &ctrl->val);
|
|
break;
|
|
case V4L2_CID_FNUMBER_RANGE:
|
|
ret = m10mo_get_fnumber_range(&dev->sd, &ctrl->val);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static struct v4l2_ctrl_ops m10mo_ctrl_ops = {
|
|
.g_volatile_ctrl = m10mo_g_volatile_ctrl,
|
|
.s_ctrl = m10mo_s_ctrl,
|
|
};
|
|
|
|
/* TODO: To move this to s_ctrl framework */
|
|
static int m10mo_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
int index;
|
|
int mode = M10MO_GET_RESOLUTION_MODE(dev->fw_type);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
dev->run_mode = param->parm.capture.capturemode;
|
|
switch (dev->run_mode) {
|
|
case CI_MODE_STILL_CAPTURE:
|
|
index = M10MO_MODE_CAPTURE_INDEX;
|
|
break;
|
|
default:
|
|
index = M10MO_MODE_PREVIEW_INDEX;
|
|
break;
|
|
}
|
|
dev->entries_curr_table = resolutions_sizes[mode][index];
|
|
dev->curr_res_table = resolutions[mode][index];
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return 0;
|
|
}
|
|
|
|
static long m10mo_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
|
|
void *arg)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct atomisp_ext_isp_ctrl *m10mo_ctrl
|
|
= (struct atomisp_ext_isp_ctrl *)arg;
|
|
int ret = 0;
|
|
|
|
dev_info(&client->dev, "m10mo ioctl id 0x%x data 0x%x\n",
|
|
m10mo_ctrl->id, m10mo_ctrl->data);
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
switch(m10mo_ctrl->id)
|
|
{
|
|
case EXT_ISP_CID_ISO:
|
|
dev_info(&client->dev, "m10mo ioctl ISO\n");
|
|
break;
|
|
case EXT_ISP_CID_CAPTURE_HDR:
|
|
ret = m10mo_set_hdr_mode(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_CAPTURE_LLS:
|
|
ret = m10mo_set_lls_mode(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_FOCUS_MODE:
|
|
ret = m10mo_set_af_mode(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_FOCUS_EXECUTION:
|
|
ret = m10mo_set_af_execution(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_TOUCH_POSX:
|
|
ret = m10mo_set_af_position_x(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_TOUCH_POSY:
|
|
ret = m10mo_set_af_position_y(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_CAF_STATUS:
|
|
ret = m10mo_get_caf_status(sd, &m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_AF_STATUS:
|
|
ret = m10mo_get_af_status(sd, &m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_GET_AF_MODE:
|
|
ret = m10mo_get_af_mode(sd, &m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_CAPTURE_BURST:
|
|
ret = dev->fw_ops->set_burst_mode(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_FLASH_MODE:
|
|
ret = m10mo_set_flash_mode(sd, m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_ZOOM:
|
|
m10mo_ctrl->data = clamp_t(unsigned int, m10mo_ctrl->data,
|
|
ZOOM_POS_MIN, ZOOM_POS_MAX);
|
|
ret = m10mo_writeb(sd, CATEGORY_MONITOR, MONITOR_ZOOM,
|
|
m10mo_ctrl->data);
|
|
break;
|
|
case EXT_ISP_CID_SHOT_MODE:
|
|
ret = m10mo_set_shot_mode(sd, m10mo_ctrl->data);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
dev_err(&client->dev, "m10mo ioctl: Unsupported ID\n");
|
|
};
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static const struct v4l2_ctrl_config ctrls[] = {
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_LINK_FREQ,
|
|
.name = "Link Frequency",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = 1,
|
|
.max = 1500000 * 1000,
|
|
.step = 1,
|
|
.def = 1,
|
|
.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
|
|
},
|
|
{
|
|
.ops = &m10mo_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,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_EXPOSURE_METERING,
|
|
.name = "Metering",
|
|
.type = V4L2_CTRL_TYPE_MENU,
|
|
.min = 0,
|
|
.max = 2,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_START_ZSL_CAPTURE,
|
|
.name = "Start zsl capture",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = 0,
|
|
.def = 1,
|
|
.max = 4,
|
|
.step = 1,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
|
|
.name = "White Balance, Auto & Preset",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = V4L2_WHITE_BALANCE_MANUAL,
|
|
.def = V4L2_WHITE_BALANCE_AUTO,
|
|
.max = V4L2_WHITE_BALANCE_SHADE,
|
|
.step = 1,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_EXPOSURE,
|
|
.name = "Exposure Bias",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = M10MO_MIN_EV,
|
|
.def = 0,
|
|
.max = M10MO_MAX_EV,
|
|
.step = M10MO_EV_STEP
|
|
},
|
|
{
|
|
.id = V4L2_CID_ISO_SENSITIVITY,
|
|
.name = "Iso",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = 100,
|
|
.def = 100,
|
|
.max = 1600,
|
|
.step = 100,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_ISO_SENSITIVITY_AUTO,
|
|
.name = "Iso mode",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = V4L2_ISO_SENSITIVITY_MANUAL,
|
|
.def = V4L2_ISO_SENSITIVITY_AUTO,
|
|
.max = V4L2_ISO_SENSITIVITY_AUTO,
|
|
.step = 1,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_COLORFX,
|
|
.name = "Image Color Effect",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = V4L2_COLORFX_NONE,
|
|
.def = V4L2_COLORFX_NONE,
|
|
.max = V4L2_COLORFX_PURPLE,
|
|
.step = 1,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_COLORFX_CBCR,
|
|
.name = "Image Color Effect CbCr",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = 0,
|
|
.def = 0,
|
|
.max = 0xffff,
|
|
.step = 1,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_TEST_PATTERN,
|
|
.name = "Test Pattern (Color Bar)",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.min = 0,
|
|
.def = 0,
|
|
.max = 1,
|
|
.step = 1
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_FOCAL_ABSOLUTE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "focal length",
|
|
.min = M10MO_FOCAL_LENGTH_DEFAULT,
|
|
.max = M10MO_FOCAL_LENGTH_DEFAULT,
|
|
.step = 1,
|
|
.def = M10MO_FOCAL_LENGTH_DEFAULT,
|
|
.flags = 0,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_FNUMBER_ABSOLUTE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "f-number",
|
|
.min = M10MO_F_NUMBER_DEFAULT,
|
|
.max = M10MO_F_NUMBER_DEFAULT,
|
|
.step = 1,
|
|
.def = M10MO_F_NUMBER_DEFAULT,
|
|
.flags = 0,
|
|
},
|
|
{
|
|
.ops = &m10mo_ctrl_ops,
|
|
.id = V4L2_CID_FNUMBER_RANGE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "f-number range",
|
|
.min = M10MO_F_NUMBER_RANGE,
|
|
.max = M10MO_F_NUMBER_RANGE,
|
|
.step = 1,
|
|
.def = M10MO_F_NUMBER_RANGE,
|
|
.flags = 0,
|
|
},
|
|
};
|
|
|
|
static int __m10mo_init_ctrl_handler(struct m10mo_device *dev)
|
|
{
|
|
struct v4l2_ctrl_handler *hdl;
|
|
int ret, i;
|
|
|
|
hdl = &dev->ctrl_handler;
|
|
|
|
ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls));
|
|
if (ret)
|
|
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) {
|
|
ret = dev->ctrl_handler.error;
|
|
v4l2_ctrl_handler_free(&dev->ctrl_handler);
|
|
return ret;
|
|
}
|
|
|
|
dev->ctrl_handler.lock = &dev->input_lock;
|
|
dev->sd.ctrl_handler = hdl;
|
|
v4l2_ctrl_handler_setup(hdl);
|
|
|
|
dev->link_freq = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_LINK_FREQ);
|
|
if (NULL == dev->link_freq)
|
|
return -ENODEV;
|
|
v4l2_ctrl_s_ctrl(dev->link_freq, V4L2_CID_LINK_FREQ);
|
|
|
|
dev->zsl_capture = v4l2_ctrl_find(&dev->ctrl_handler,
|
|
V4L2_CID_START_ZSL_CAPTURE);
|
|
if (NULL == dev->zsl_capture)
|
|
return -ENODEV;
|
|
v4l2_ctrl_s_ctrl(dev->zsl_capture, V4L2_CID_START_ZSL_CAPTURE);
|
|
|
|
dev->colorfx = v4l2_ctrl_find(&dev->ctrl_handler,
|
|
V4L2_CID_COLORFX);
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct atomisp_camera_caps *caps =
|
|
dev->pdata->common.get_camera_caps();
|
|
|
|
/* Select operating sensor. */
|
|
if (caps->sensor_num > 1) {
|
|
return m10mo_write(sd, 1, CATEGORY_SYSTEM,
|
|
SYSTEM_MASTER_SENSOR, !output);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_s_frame_interval(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_frame_interval *interval)
|
|
{
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
int fps = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (interval->interval.numerator != 0)
|
|
fps = interval->interval.denominator / interval->interval.numerator;
|
|
if (!fps) {
|
|
mutex_unlock(&dev->input_lock);
|
|
return -EINVAL;
|
|
}
|
|
dev->fps = fps;
|
|
dev_dbg(&client->dev, "%s: fps is %d\n", __func__, dev->fps);
|
|
mutex_unlock(&dev->input_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_subdev_video_ops m10mo_video_ops = {
|
|
.try_mbus_fmt = m10mo_try_mbus_fmt,
|
|
.s_mbus_fmt = m10mo_set_mbus_fmt,
|
|
.g_mbus_fmt = m10mo_get_mbus_fmt,
|
|
.s_stream = m10mo_s_stream,
|
|
.s_parm = m10mo_s_parm,
|
|
.enum_framesizes = m10mo_enum_framesizes,
|
|
.s_routing = m10mo_s_routing,
|
|
.s_frame_interval = m10mo_s_frame_interval,
|
|
};
|
|
|
|
static const struct v4l2_subdev_core_ops m10mo_core_ops = {
|
|
.g_ctrl = v4l2_subdev_g_ctrl,
|
|
.s_ctrl = v4l2_subdev_s_ctrl,
|
|
.s_power = m10mo_s_power,
|
|
.init = m10mo_fw_start,
|
|
.ioctl = m10mo_ioctl,
|
|
};
|
|
|
|
static const struct v4l2_subdev_pad_ops m10mo_pad_ops = {
|
|
.enum_mbus_code = m10mo_enum_mbus_code,
|
|
.get_fmt = m10mo_get_pad_format,
|
|
.set_fmt = m10mo_set_pad_format,
|
|
|
|
};
|
|
|
|
static const struct v4l2_subdev_ops m10mo_ops = {
|
|
.core = &m10mo_core_ops,
|
|
.pad = &m10mo_pad_ops,
|
|
.video = &m10mo_video_ops,
|
|
};
|
|
|
|
static int dump_fw(struct m10mo_device *dev)
|
|
{
|
|
int ret = 0;
|
|
mutex_lock(&dev->input_lock);
|
|
if (dev->power == 1) {
|
|
ret = -EBUSY;
|
|
goto leave;
|
|
}
|
|
__m10mo_s_power(&dev->sd, 1, true);
|
|
m10mo_dump_fw(dev);
|
|
__m10mo_s_power(&dev->sd, 0, true);
|
|
leave:
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int read_fw_checksum(struct m10mo_device *dev, u16 *result)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (dev->power == 1) {
|
|
ret = -EBUSY;
|
|
goto leave;
|
|
}
|
|
__m10mo_s_power(&dev->sd, 1, true);
|
|
ret = m10mo_fw_checksum(dev, result);
|
|
__m10mo_s_power(&dev->sd, 0, true);
|
|
leave:
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int read_fw_version(struct m10mo_device *dev, char *buf)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (dev->power == 1) {
|
|
ret = -EBUSY;
|
|
goto leave;
|
|
}
|
|
__m10mo_s_power(&dev->sd, 1, true);
|
|
ret = m10mo_get_isp_fw_version_string(dev, buf, M10MO_MAX_FW_ID_STRING,
|
|
dev->fw_addr_id);
|
|
__m10mo_s_power(&dev->sd, 0, true);
|
|
leave:
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int update_fw(struct m10mo_device *dev)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
|
|
int ret = 0;
|
|
u16 result;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
if (dev->power == 1) {
|
|
ret = -EBUSY;
|
|
goto leave;
|
|
}
|
|
__m10mo_s_power(&dev->sd, 1, true);
|
|
ret = m10mo_program_device(dev);
|
|
__m10mo_s_power(&dev->sd, 0, true);
|
|
if (ret)
|
|
goto leave;
|
|
/* Power cycle chip and re-identify the version */
|
|
__m10mo_s_power(&dev->sd, 1, true);
|
|
ret = m10mo_identify_fw_type(&dev->sd);
|
|
__m10mo_s_power(&dev->sd, 0, true);
|
|
if (ret)
|
|
goto leave;
|
|
|
|
__m10mo_s_power(&dev->sd, 1, true);
|
|
ret = m10mo_fw_checksum(dev, &result);
|
|
__m10mo_s_power(&dev->sd, 0, true);
|
|
dev_info(&client->dev, "m10mo FW checksum: %d\n", result);
|
|
|
|
leave:
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t m10mo_flash_rom_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "flash\n");
|
|
}
|
|
|
|
static ssize_t m10mo_flash_rom_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t len)
|
|
{
|
|
struct m10mo_device *m10mo_dev = dev_get_drvdata(dev);
|
|
|
|
if (!strncmp(buf, "flash", 5)) {
|
|
update_fw(m10mo_dev);
|
|
return len;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
static DEVICE_ATTR(isp_flashfw, S_IRUGO | S_IWUSR, m10mo_flash_rom_show,
|
|
m10mo_flash_rom_store);
|
|
|
|
static ssize_t m10mo_flash_spi_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct m10mo_device *m10mo_dev = dev_get_drvdata(dev);
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"%d\n", m10mo_get_spi_state(m10mo_dev));
|
|
}
|
|
|
|
static ssize_t m10mo_flash_spi_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t len)
|
|
{
|
|
struct m10mo_device *m10mo_dev = dev_get_drvdata(dev);
|
|
unsigned long value;
|
|
int ret;
|
|
|
|
if (strict_strtoul(buf, 0, &value))
|
|
return -EINVAL;
|
|
|
|
ret = m10mo_set_spi_state(m10mo_dev, value);
|
|
if (ret)
|
|
return ret;
|
|
return len;
|
|
}
|
|
static DEVICE_ATTR(isp_spi, S_IRUGO | S_IWUSR, m10mo_flash_spi_show,
|
|
m10mo_flash_spi_store);
|
|
|
|
static ssize_t m10mo_flash_checksum_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct m10mo_device *m10_dev = dev_get_drvdata(dev);
|
|
ssize_t ret;
|
|
u16 result;
|
|
|
|
ret = read_fw_checksum(m10_dev, &result);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%04x\n", result);
|
|
}
|
|
static DEVICE_ATTR(isp_checksum, S_IRUGO, m10mo_flash_checksum_show, NULL);
|
|
|
|
static ssize_t m10mo_flash_version_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct m10mo_device *m10_dev = dev_get_drvdata(dev);
|
|
ssize_t ret;
|
|
|
|
ret = read_fw_version(m10_dev, buf);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s\n", buf);
|
|
}
|
|
static DEVICE_ATTR(isp_version, S_IRUGO, m10mo_flash_version_show, NULL);
|
|
|
|
static ssize_t m10mo_flash_dump_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct m10mo_device *m10_dev = dev_get_drvdata(dev);
|
|
dump_fw(m10_dev);
|
|
return scnprintf(buf, PAGE_SIZE, "done\n");
|
|
}
|
|
static DEVICE_ATTR(isp_fw_dump, S_IRUGO, m10mo_flash_dump_show, NULL);
|
|
|
|
static int m10mo_ispd1(struct m10mo_device *dev)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
|
|
struct v4l2_subdev *sd = &dev->sd;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
dev_info(&client->dev, "ispd1 start\n");
|
|
ret = m10mo_dump_string_log1(sd);
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "isp log 1 error\n");
|
|
|
|
dev_info(&client->dev, "ispd1 finished\n");
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_ispd2(struct m10mo_device *dev)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
|
|
struct v4l2_subdev *sd = &dev->sd;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
dev_info(&client->dev, "ispd2 start\n");
|
|
|
|
ret = m10mo_dump_string_log2_1(sd);
|
|
if (ret != 0)
|
|
dev_err(&client->dev, "m10mo_dump_string_log2_1 error\n");
|
|
|
|
dev_info(&client->dev, "log2_1 finished\n");
|
|
|
|
ret = m10mo_dump_string_log2_2(sd);
|
|
if (ret != 0)
|
|
dev_err(&client->dev, "m10mo_dump_string_log2_2 error\n");
|
|
|
|
dev_info(&client->dev, "ispd2_2 finish\n");
|
|
|
|
ret = m10mo_dump_string_log2_3(sd);
|
|
if (ret != 0)
|
|
dev_err(&client->dev, "m10mo_dump_string_log2_3 error\n");
|
|
|
|
dev_info(&client->dev, "ispd2_3 finish\n");
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_ispd3(struct m10mo_device *dev)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
|
|
struct v4l2_subdev *sd = &dev->sd;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
/**ISP RESET**/
|
|
ret = dev->pdata->common.gpio_ctrl(sd, 0);
|
|
|
|
msleep(10);
|
|
|
|
/**ISP RESET**/
|
|
ret = dev->pdata->common.gpio_ctrl(sd, 1);
|
|
|
|
msleep(50);
|
|
|
|
dev_info(&client->dev, "ispd3 start\n");
|
|
|
|
ret = m10mo_dump_string_log2_1(sd);
|
|
if (ret != 0)
|
|
dev_err(&client->dev, "m10mo_dump_string_log2_1 error\n");
|
|
|
|
dev_info(&client->dev, "log2_1 finished\n");
|
|
|
|
ret = m10mo_dump_string_log2_2(sd);
|
|
if (ret != 0)
|
|
dev_err(&client->dev, "m10mo_dump_string_log2_2 error\n");
|
|
|
|
dev_info(&client->dev, "ispd2_2 finish\n");
|
|
|
|
ret = m10mo_dump_string_log2_3(sd);
|
|
if (ret != 0)
|
|
dev_err(&client->dev, "m10mo_dump_string_log2_3 error\n");
|
|
|
|
dev_info(&client->dev, "ispd2_3 finish\n");
|
|
|
|
dev_info(&client->dev, "ispd3 finished\n");
|
|
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int m10mo_ispd4(struct m10mo_device *dev)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
|
|
struct v4l2_subdev *sd = &dev->sd;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&dev->input_lock);
|
|
|
|
dev_info(&client->dev, "ispd4 start\n");
|
|
ret = m10mo_dump_string_log3(sd);
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "isp log 4 error\n");
|
|
|
|
dev_info(&client->dev, "ispd4 finished\n");
|
|
mutex_unlock(&dev->input_lock);
|
|
return ret;
|
|
}
|
|
|
|
void m10mo_dump_log(struct v4l2_subdev *sd)
|
|
{
|
|
struct m10mo_device *m10mo_dev = to_m10mo_sensor(sd);
|
|
|
|
/*
|
|
* dbglvl: bit0 to dump m10mo_ispd1
|
|
* dbglvl: bit1 to dump m10mo_ispd2
|
|
* dbglvl: bit2 to dump m10mo_ispd4
|
|
* Debug log 3 is most important, so dump first
|
|
*/
|
|
if (dbglvl & 4)
|
|
m10mo_ispd4(m10mo_dev);
|
|
|
|
if (dbglvl & 2)
|
|
m10mo_ispd2(m10mo_dev);
|
|
|
|
if (dbglvl & 1)
|
|
m10mo_ispd1(m10mo_dev);
|
|
}
|
|
|
|
static ssize_t m10mo_isp_log_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t len)
|
|
{
|
|
struct m10mo_device *m10mo_dev = dev_get_drvdata(dev);
|
|
|
|
if (!strncmp(buf, "1", 1))
|
|
m10mo_ispd1(m10mo_dev);
|
|
else if (!strncmp(buf, "4", 1))
|
|
m10mo_ispd4(m10mo_dev);
|
|
else if (!strncmp(buf, "2", 1))
|
|
m10mo_ispd2(m10mo_dev);
|
|
else if (!strncmp(buf, "3", 1))
|
|
m10mo_ispd3(m10mo_dev);
|
|
else
|
|
m10mo_ispd4(m10mo_dev);
|
|
|
|
return len;
|
|
}
|
|
|
|
static DEVICE_ATTR(isp_log, S_IRUGO | S_IWUSR, NULL, m10mo_isp_log_store);
|
|
|
|
static struct attribute *sysfs_attrs_ctrl[] = {
|
|
&dev_attr_isp_flashfw.attr,
|
|
&dev_attr_isp_checksum.attr,
|
|
&dev_attr_isp_fw_dump.attr,
|
|
&dev_attr_isp_spi.attr,
|
|
&dev_attr_isp_version.attr,
|
|
&dev_attr_isp_log.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group m10mo_attribute_group[] = {
|
|
{.attrs = sysfs_attrs_ctrl },
|
|
};
|
|
|
|
static int m10mo_remove(struct i2c_client *client)
|
|
{
|
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
struct m10mo_device *dev = to_m10mo_sensor(sd);
|
|
|
|
sysfs_remove_group(&client->dev.kobj,
|
|
m10mo_attribute_group);
|
|
|
|
if (dev->pdata->common.platform_deinit)
|
|
dev->pdata->common.platform_deinit();
|
|
|
|
media_entity_cleanup(&dev->sd.entity);
|
|
v4l2_device_unregister_subdev(sd);
|
|
free_irq(client->irq, sd);
|
|
kfree(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m10mo_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct m10mo_device *dev;
|
|
struct camera_mipi_info *mipi_info = NULL;
|
|
int ret;
|
|
|
|
if (!client->dev.platform_data) {
|
|
dev_err(&client->dev, "platform data missing\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* allocate sensor device & init sub device */
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev) {
|
|
dev_err(&client->dev, "%s: out of memory\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*
|
|
* This I2C device is created by atomisp driver. Atomisp driver has
|
|
* a nasty assumption that all sensors uses similar platform data.
|
|
* For this driver we need more information. Platform data what was
|
|
* received with I2C device data structure points to the common
|
|
* structure. Pick the real platform data for this driver.
|
|
*/
|
|
dev->pdata = container_of(client->dev.platform_data,
|
|
struct m10mo_platform_data,
|
|
common);
|
|
|
|
dev->mode = M10MO_POWERED_OFF;
|
|
dev->requested_mode = M10MO_NO_MODE_REQUEST;
|
|
dev->iso_sensitivity = REG_AE_ISOMODE_ISO100;
|
|
dev->iso_mode = V4L2_ISO_SENSITIVITY_AUTO;
|
|
dev->monitor_params.af_mode = AF_NORMAL;
|
|
dev->monitor_params.exe_mode = AF_STOP;
|
|
|
|
mutex_init(&dev->input_lock);
|
|
|
|
v4l2_i2c_subdev_init(&(dev->sd), client, &m10mo_ops);
|
|
|
|
ret = m10mo_s_config(&dev->sd, client->irq);
|
|
if (ret)
|
|
goto out_free;
|
|
|
|
/*
|
|
* We must have a way to reset the chip. If that is missing we
|
|
* simply can't continue.
|
|
*/
|
|
if (!dev->pdata->common.gpio_ctrl) {
|
|
dev_err(&client->dev, "gpio control function missing\n");
|
|
ret = -ENODEV;
|
|
goto out_free_irq;
|
|
}
|
|
|
|
mipi_info = v4l2_get_subdev_hostdata(&dev->sd);
|
|
|
|
ret = __m10mo_init_ctrl_handler(dev);
|
|
if (ret)
|
|
goto out_free_irq;
|
|
|
|
if (mipi_info)
|
|
dev->num_lanes = mipi_info->num_lanes;
|
|
|
|
dev->curr_res_table = resolutions[0][M10MO_MODE_PREVIEW_INDEX];
|
|
dev->entries_curr_table = resolutions_sizes[0][M10MO_MODE_PREVIEW_INDEX];
|
|
|
|
dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
dev->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
dev->sd.entity.ops = &m10mo_entity_ops;
|
|
dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
|
dev->format.code = V4L2_MBUS_FMT_UYVY8_1X16;
|
|
dev->shot_mode = SHOT_MODE_AUTO;
|
|
|
|
ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0);
|
|
if (ret)
|
|
goto out_free_irq;
|
|
|
|
ret = sysfs_create_group(&client->dev.kobj,
|
|
m10mo_attribute_group);
|
|
if (ret) {
|
|
dev_err(&client->dev, "%s Failed to create sysfs\n", __func__);
|
|
goto out_sysfs_fail;
|
|
}
|
|
|
|
/* Request SPI interface to enable FW update over the SPI */
|
|
if (dev->pdata->spi_setup)
|
|
dev->pdata->spi_setup(&dev->pdata->spi_pdata, dev);
|
|
|
|
return 0;
|
|
|
|
out_sysfs_fail:
|
|
media_entity_cleanup(&dev->sd.entity);
|
|
out_free_irq:
|
|
free_irq(client->irq, &dev->sd);
|
|
out_free:
|
|
v4l2_device_unregister_subdev(&dev->sd);
|
|
kfree(dev);
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_device_id m10mo_id[] = {
|
|
{ M10MO_NAME, 0 },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, m10mo_id);
|
|
|
|
static struct i2c_driver m10mo_driver = {
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = M10MO_NAME,
|
|
},
|
|
.probe = m10mo_probe,
|
|
.remove = m10mo_remove,
|
|
.id_table = m10mo_id,
|
|
};
|
|
|
|
static __init int init_m10mo(void)
|
|
{
|
|
return i2c_add_driver(&m10mo_driver);
|
|
}
|
|
|
|
static __exit void exit_m10mo(void)
|
|
{
|
|
i2c_del_driver(&m10mo_driver);
|
|
}
|
|
|
|
module_init(init_m10mo);
|
|
module_exit(exit_m10mo);
|
|
|
|
MODULE_DESCRIPTION("M10MO ISP driver");
|
|
MODULE_AUTHOR("Kriti Pachhandara <kriti.pachhandara@intel.com>");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|