// SPDX-License-Identifier: GPL-2.0
/*
* Support for OmniVision OV2722 1080p HD camera sensor.
*
* Copyright (c) 2013 Intel Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/moduleparam.h>
#include <media/v4l2-device.h>
#include "../include/linux/atomisp_gmin_platform.h"
#include <linux/acpi.h>
#include <linux/io.h>
#include "ov2722.h"
/* i2c read/write stuff */
static int ov2722_read_reg(struct i2c_client *client,
u16 data_length, u16 reg, u16 *val)
{
int err;
struct i2c_msg msg[2];
unsigned char data[6];
if (!client->adapter) {
dev_err(&client->dev, "%s error, no client->adapter\n",
__func__);
return -ENODEV;
}
if (data_length != OV2722_8BIT && data_length != OV2722_16BIT &&
data_length != OV2722_32BIT) {
dev_err(&client->dev, "%s error, invalid data length\n",
__func__);
return -EINVAL;
}
memset(msg, 0, sizeof(msg));
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = I2C_MSG_LENGTH;
msg[0].buf = data;
/* high byte goes out first */
data[0] = (u8)(reg >> 8);
data[1] = (u8)(reg & 0xff);
msg[1].addr = client->addr;
msg[1].len = data_length;
msg[1].flags = I2C_M_RD;
msg[1].buf = data;
err = i2c_transfer(client->adapter, msg, 2);
if (err != 2) {
if (err >= 0)
err = -EIO;
dev_err(&client->dev,
"read from offset 0x%x error %d", reg, err);
return err;
}
*val = 0;
/* high byte comes first */
if (data_length == OV2722_8BIT)
*val = (u8)data[0];
else if (data_length == OV2722_16BIT)
*val = be16_to_cpu(*(__be16 *)&data[0]);
else
*val = be32_to_cpu(*(__be32 *)&data[0]);
return 0;
}
static int ov2722_i2c_write(struct i2c_client *client, u16 len, u8 *data)
{
struct i2c_msg msg;
const int num_msg = 1;
int ret;
msg.addr = client->addr;
msg.flags = 0;
msg.len = len;
msg.buf = data;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret == num_msg ? 0 : -EIO;
}
static int ov2722_write_reg(struct i2c_client *client, u16 data_length,
u16 reg, u16 val)
{
int ret;
unsigned char data[4] = {0};
__be16 *wreg = (__be16 *)data;
const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
if (data_length != OV2722_8BIT && data_length != OV2722_16BIT) {
dev_err(&client->dev,
"%s error, invalid data_length\n", __func__);
return -EINVAL;
}
/* high byte goes out first */
*wreg = cpu_to_be16(reg);
if (data_length == OV2722_8BIT) {
data[2] = (u8)(val);
} else {
/* OV2722_16BIT */
__be16 *wdata = (__be16 *)&data[2];
*wdata = cpu_to_be16(val);
}
ret = ov2722_i2c_write(client, len, data);
if (ret)
dev_err(&client->dev,
"write error: wrote 0x%x to offset 0x%x error %d",
val, reg, ret);
return ret;
}
/*
* ov2722_write_reg_array - Initializes a list of OV2722 registers
* @client: i2c driver client structure
* @reglist: list of registers to be written
*
* This function initializes a list of registers. When consecutive addresses
* are found in a row on the list, this function creates a buffer and sends
* consecutive data in a single i2c_transfer().
*
* __ov2722_flush_reg_array, __ov2722_buf_reg_array() and
* __ov2722_write_reg_is_consecutive() are internal functions to
* ov2722_write_reg_array_fast() and should be not used anywhere else.
*
*/
static int __ov2722_flush_reg_array(struct i2c_client *client,
struct ov2722_write_ctrl *ctrl)
{
u16 size;
__be16 *data16 = (void *)&ctrl->buffer.addr;
if (ctrl->index == 0)
return 0;
size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
*data16 = cpu_to_be16(ctrl->buffer.addr);
ctrl->index = 0;
return ov2722_i2c_write(client, size, (u8 *)&ctrl->buffer);
}
static int __ov2722_buf_reg_array(struct i2c_client *