ov2640子设备核心操作详细分析
文章目录
ov2640_subdev_core_ops核心操作
// ov2640子设备核心操作
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov2640_g_register, // 获取寄存器值
.s_register = ov2640_s_register, // 设置寄存器值
#endif
.s_power = ov2640_s_power, // 设置ov2640的电源
};
获取寄存器值ov2640_g_register
函数名为ov2640_g_register,接受一个v4l2_subdev结构体指针和一个v4l2_dbg_register结构体指针reg作为参数。
函数的主要功能如下:
从v4l2_subdev结构体中获取i2c_client结构体指针,该结构体用于表示I2C设备。
设置要读取的寄存器的大小为1字节,并检查要读取的寄存器地址是否有效,如果无效则返回-EINVAL表示无效参数。
使用i2c_smbus_read_byte_data函数通过I2C总线读取指定寄存器的值。
如果读取成功,将读取的值赋给reg->val字段,并返回0表示成功。
如果读取失败,直接返回读取函数的返回值。
该函数的作用是通过使用I2C总线读取指定寄存器的值,并将读取的值存储在reg->val字段中,以实现获取寄存器值的功能。
// 获取指定寄存器的值
static int ov2640_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
// 获取i2c_client结构体
struct i2c_client *client = v4l2_get_subdevdata(sd);
// 定义变量
int ret;
reg->size = 1;
if (reg->reg > 0xff)
return -EINVAL;
// 通过I2C总线读取指定寄存器的值
ret = i2c_smbus_read_byte_data(client, reg->reg);
if (ret < 0)
return ret;
reg->val = ret;
return 0;
}
函数名为 i2c_smbus_read_byte_data,接受一个 i2c_client 结构体指针 client 和一个 u8 类型的命令 command 作为参数。
函数的主要功能如下:
定义一个联合体变量 data,用于存储读取的数据。
定义一个整型变量 status,用于存储函数执行的状态。
调用 i2c_smbus_xfer 函数,传入适配器、地址、标志位,以及读取操作的参数,包括命令 command 和数据类型为字节型。
执行读取操作,并将读取的数据存储在 data 中。
返回执行结果,如果 status 小于 0,返回 status,否则返回 data.byte。
该函数的作用是通过使用 I2C 总线进行读取操作,读取指定地址的寄存器的字节数据,并返回读取的结果。
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
{
union i2c_smbus_data data; // 定义一个联合体变量data
int status; // 定义一个整型变量status
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, // 调用i2c_smbus_xfer函数,传入适配器、地址、标志位
I2C_SMBUS_READ, command, // 读取操作,命令为command
I2C_SMBUS_BYTE_DATA, &data); // 读取一个字节的数据,存储在data中
return (status < 0) ? status : data.byte; // 如果status小于0,返回status,否则返回data.byte
}
设置寄存器值ov2640_s_register
这段代码用于设置指定寄存器的值。
函数名为ov2640_s_register,接受一个v4l2_subdev结构体指针和一个v4l2_dbg_register结构体指针reg作为参数。
函数的主要功能如下:
从v4l2_subdev结构体中获取i2c_client结构体指针,该结构体用于表示I2C设备。
检查要设置的寄存器地址和值是否超出有效范围,如果超出范围,则返回-EINVAL表示无效参数。
使用i2c_smbus_write_byte_data函数通过I2C总线向指定寄存器写入指定的值。
返回i2c_smbus_write_byte_data函数的返回值。
该函数的作用是通过使用I2C总线向指定的寄存器写入指定的值,以实现设置寄存器的功能。
// 设置指定寄存器的值
static int ov2640_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
// 获取i2c_client结构体
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff ||
reg->val > 0xff)
return -EINVAL;
// 通过I2C总线向指定寄存器写入指定值
return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
}
/driver/i2c/i2c-core.c
函数名为 i2c_smbus_write_byte_data,接受一个 i2c_client 结构体指针 client、一个 u8 类型的命令 command,以及一个 u8 类型的值 value 作为参数。
函数的主要功能如下:
定义一个联合体变量 data,用于存储要写入的数据。
将参数 value 的值存储在 data 中。
调用 i2c_smbus_xfer 函数,传入适配器、地址、标志位,以及写入操作的参数,包括命令 command 和数据类型为字节型,以及存储要写入数据的地址 &data。
执行写入操作,并将结果返回。
该函数的作用是通过使用 I2C 总线进行写入操作,将指定地址的寄存器写入一个字节的数据,返回写入操作的结果。
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)
{
union i2c_smbus_data data; // 定义一个联合体变量data
data.byte = value; // 将value存储在data中
return i2c_smbus_xfer(client->adapter, client->addr, client->flags, // 调用i2c_smbus_xfer函数,传入适配器、地址、标志位
I2C_SMBUS_WRITE, command, // 写入操作,命令为command
I2C_SMBUS_BYTE_DATA, &data); // 写入一个字节的数据,存储在data中
}
i2c_smbus_xfer
Linux内核驱动程序:使用模拟I2C
Linux-4.9.88\drivers\i2c\busses\i2c-gpio.c
Linux-5.4\drivers\i2c\busses\i2c-gpio.c
Linux内核真正的I2C控制器驱动程序
IMX6ULL: Linux-4.9.88\drivers\i2c\busses\i2c-imx.c
ov2640_s_register->i2c_smbus_write_byte_data->i2c_smbus_xfer
函数名为 i2c_smbus_xfer,接受一个 i2c_adapter 结构体指针 adapter、一个 u16 类型的地址 addr、一个 unsigned short 类型的标志位 flags、一个 char 类型的读写操作 read_write、一个 u8 类型的命令 command、一个 int 类型的协议 protocol,以及一个 union i2c_smbus_data 联合体指针 data 作为参数。
函数的主要功能如下:
执行跟踪点追踪,记录有关 SMBus 写入和读取操作的信息。
对标志位进行处理,只保留 I2C_M_TEN、I2C_CLIENT_PEC 和 I2C_CLIENT_SCCB 三个标志位。
如果适配器的算法中实现了 smbus_xfer 函数,则使用该函数进行 SMBus 传输。在仲裁丢失的情况下自动重试,直到达到最大重试次数或超时。
如果适配器的算法没有实现 smbus_xfer 函数或返回 -EOPNOTSUPP,则回退到使用 i2c_smbus_xfer_emulated 函数进行传输。
执行跟踪点追踪,记录有关 SMBus 回复和结果的信息。
返回传输的结果。
该函数的作用是通过使用 I2C 总线进行 SMBus 传输。它根据适配器的支持情况选择合适的传输方法,并处理重试和回退逻辑,最终返回传输的结果。
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
{
unsigned long orig_jiffies;
int try;
s32 res;
/* 如果启用,则以下两个跟踪点取决于读写和协议。*/
trace_smbus_write(adapter, addr, flags, read_write,
command, protocol, data);
trace_smbus_read(adapter, addr, flags, read_write,
command, protocol);
/* 仅保留I2C_M_TEN、I2C_CLIENT_PEC和I2C_CLIENT_SCCB标志 */
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
if (adapter->algo->smbus_xfer) {
i2c_lock_adapter(adapter);
/* 在仲裁丢失的情况下自动重试 */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags,
read_write, command,
protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
i2c_unlock_adapter(adapter);
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
goto trace;
/*
* 如果适配器没有实现SMBus操作的本地支持,则回退到i2c_smbus_xfer_emulated。
*/
}
res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data);
trace:
/* 如果启用,则回复跟踪点取决于读写。*/
trace_smbus_reply(adapter, addr, flags, read_write,
command, protocol, data);
trace_smbus_result(adapter, addr, flags, read_write,
command, protocol, res);
return res;
}
i2c_imx_xfer
smbus_xfer进行 SMBus 传输
/driver/i2c/busses/i2c-imx.c
这个函数是用于在i.MX平台的I2C适配器上执行数据传输操作。它是i.MX系列芯片的I2C驱动程序中的一部分。
该函数的作用如下:
开始I2C传输:调用i2c_imx_start函数启动I2C传输。它会发送起始条件(START)信号。
读写数据:对于每个消息(msgs数组中的每个元素),根据消息的属性进行读写操作。如果消息的flags标志指示要读取数据,则调用i2c_imx_read函数进行读取;否则,根据DMA的可用性调用适当的写入函数进行写入操作,可以选择使用DMA写入或普通写入。
停止I2C传输:调用i2c_imx_stop函数停止I2C传输。它会发送停止条件(STOP)信号。
返回结果:根据传输的成功与否,返回相应的结果。如果传输失败,则返回负数表示错误;否则,返回传输的消息数量。
该函数在I2C传输过程中还输出一些调试信息,例如控制寄存器(I2CR)和状态寄存器(I2SR)的值,以及传输消息的序号和结果状态。
总体而言,这个函数负责管理i.MX平台上的I2C传输过程,处理起始条件、读写数据以及停止条件,以实现与I2C设备的通信。
static int i2c_imx_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
unsigned int i, temp;
int result;
bool is_lastmsg = false;
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
/* Start I2C transfer */
result = i2c_imx_start(i2c_imx); // 开始I2C传输
if (result)
goto fail0;
/* read/write data */
for (i = 0; i < num; i++) { // 读写数据
if (i == num - 1)
is_lastmsg = true;
if (i) {
dev_dbg(&i2c_imx->adapter.dev,
"<%s> repeated start\n", __func__);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
temp |= I2CR_RSTA; // 重复启动
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
result = i2c_imx_bus_busy(i2c_imx, 1);
if (result)
goto fail0;
}
dev_dbg(&i2c_imx->adapter.dev,
"<%s> transfer message: %d\n", __func__, i);
/* write/read data */
#ifdef CONFIG_I2C_DEBUG_BUS
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
dev_dbg(&i2c_imx->adapter.dev,
"<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
__func__,
(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
dev_dbg(&i2c_imx->adapter.dev,
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
__func__,
(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
(temp & I2SR_RXAK ? 1 : 0));
#endif
if (msgs[i].flags & I2C_M_RD)
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); // 读取
else {
if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
result = i2c_imx_dma_write(i2c_imx, &msgs[i]); // DMA写
else
result = i2c_imx_write(i2c_imx, &msgs[i]); // 写
}
if (result)
goto fail0;
}
fail0:
/* Stop I2C transfer */
i2c_imx_stop(i2c_imx); // 停止I2C传输
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
(result < 0) ? result : num);
return (result < 0) ? result : num;
}
i2c_smbus_xfer_emulated
ov2640_s_register->i2c_smbus_write_byte_data->i2c_smbus_xfer->i2c_smbus_xfer_emulated
函数名为 i2c_smbus_xfer,接受一个 i2c_adapter 结构体指针 adapter、一个 u16 类型的地址 addr、一个 unsigned short 类型的标志位 flags、一个 char 类型的读写操作 read_write、一个 u8 类型的命令 command、一个 int 类型的协议 protocol,以及一个 union i2c_smbus_data 联合体指针 data 作为参数。
函数的主要功能如下:
执行跟踪点追踪,记录有关 SMBus 写入和读取操作的信息。
对标志位进行处理,只保留 I2C_M_TEN、I2C_CLIENT_PEC 和 I2C_CLIENT_SCCB 三个标志位。
如果适配器的算法中实现了 smbus_xfer 函数,则使用该函数进行 SMBus 传输。在仲裁丢失的情况下自动重试,直到达到最大重试次数或超时。
如果适配器的算法没有实现 smbus_xfer 函数或返回 -EOPNOTSUPP,则回退到使用 i2c_smbus_xfer_emulated 函数进行传输。
执行跟踪点追踪,记录有关 SMBus 回复和结果的信息。
返回传输的结果。
该函数的作用是通过使用 I2C 总线进行 SMBus 传输。它根据适配器的支持情况选择合适的传输方法,并处理重试和回退逻辑,最终返回传输的结果。
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data *data)
{
/* 需要生成一系列的消息。在写入的情况下,我们只需要使用一个消息;在读取时,我们需要两个消息。我们使用合理的默认值初始化大多数内容,以使下面的代码更简单。 */
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; // 第一个消息的缓冲区
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; // 第二个消息的缓冲区
int num = read_write == I2C_SMBUS_READ ? 2 : 1; // 消息数量
int i;
u8 partial_pec = 0; // 部分PEC
int status;
struct i2c_msg msg[2] = {
{
.addr = addr, // 地址
.flags = flags, // 标志
.len = 1, // 长度
.buf = msgbuf0, // 缓冲区
}, {
.addr = addr, // 地址
.flags = flags | I2C_M_RD, // 标志
.len = 0, // 长度
.buf = msgbuf1, // 缓冲区
},
};
msgbuf0[0] = command; // 命令
switch (size) { // 根据size的值进行判断
case I2C_SMBUS_QUICK: // 快速模式
msg[0].len = 0; // 长度为0
/* 特殊情况:读写字段用作数据 */
msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
I2C_M_RD : 0); // 标志
num = 1; // 消息数量为1
break;
case I2C_SMBUS_BYTE: // 字节模式
if (read_write == I2C_SMBUS_READ) {
/* 特殊情况:只有读! */
msg[0].flags = I2C_M_RD | flags; // 标志
num = 1; // 消息数量为1
}
break;
case I2C_SMBUS_BYTE_DATA: // 字节数据模式
if (read_write == I2C_SMBUS_READ)
msg[1].len = 1; // 长度为1
else {
msg[0].len = 2; // 长度为2
msgbuf0[1] = data->byte; // 数据
}
break;
case I2C_SMBUS_WORD_DATA: // 字数据模式
if (read_write == I2C_SMBUS_READ)
msg[1].len = 2; // 长度为2
else {
msg[0].len = 3; // 长度为3
msgbuf0[1] = data->word & 0xff; // 数据低位
msgbuf0[2] = data->word >> 8; // 数据高位
}
break;
case I2C_SMBUS_PROC_CALL: // 处理调用
num = 2; /* 特殊情况 */
read_write = I2C_SMBUS_READ; // 读操作
msg[0].len = 3; // 长度为3
msg[1].len = 2; // 长度为2
msgbuf0[1] = data->word & 0xff; // 数据低位
msgbuf0[2] = data->word >> 8; // 数据高位
break;
case I2C_SMBUS_BLOCK_DATA: // 块数据
if (read_write == I2C_SMBUS_READ) {
msg[1].flags |= I2C_M_RECV_LEN; // 接收长度
msg[1].len = 1; /* 块长度将由底层总线驱动程序添加 */
} else {
msg[0].len = data->block[0] + 2; // 长度为块长度+2
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { // 如果长度大于最大块长度+2
dev_err(&adapter->dev,
"Invalid block write size %d\n", // 输出错误信息
data->block[0]);
return -EINVAL; // 返回错误
}
for (i = 1; i < msg[0].len; i++) // 遍历块
msgbuf0[i] = data->block[i-1]; // 将块中的数据存入缓冲区
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL: // 块处理调用
num = 2; /* 另一种特殊情况 */
read_write = I2C_SMBUS_READ; // 读操作
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { // 如果块长度大于最大块长度
dev_err(&adapter->dev,
"Invalid block write size %d\n", // 输出错误信息
data->block[0]);
return -EINVAL; // 返回错误
}
msg[0].len = data->block[0] + 2; // 长度为块长度+2
for (i = 1; i < msg[0].len; i++) // 遍历块
msgbuf0[i] = data->block[i-1]; // 将块中的数据存入缓冲区
msg[1].flags |= I2C_M_RECV_LEN; // 接收长度
msg[1].len = 1; /* 块长度将由底层总线驱动程序添加 */
break;
case I2C_SMBUS_I2C_BLOCK_DATA: // I2C块数据
if (read_write == I2C_SMBUS_READ) {
msg[1].len = data->block[0]; // 长度为块长度
} else {
msg[0].len = data->block[0] + 1; // 长度为块长度+1
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) { // 如果长度大于最大块长度+1
dev_err(&adapter->dev,
"Invalid block write size %d\n", // 输出错误信息
data->block[0]);
return -EINVAL; // 返回错误
}
for (i = 1; i <= data->block[0]; i++) // 遍历块
msgbuf0[i] = data->block[i]; // 将块中的数据存入缓冲区
}
break;
default:
dev_err(&adapter->dev, "Unsupported transaction %d\n", size); // 输出错误信息
return -EOPNOTSUPP; // 返回错误
}
// 判断是否需要计算PEC
i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
if (i) {
/* 如果第一个消息是写,则计算PEC */
if (!(msg[0].flags & I2C_M_RD)) {
if (num == 1) /* 只有写操作 */
i2c_smbus_add_pec(&msg[0]);
else /* 先写后读 */
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
}
/* 如果最后一个消息是读,则请求PEC */
if (msg[num-1].flags & I2C_M_RD)
msg[num-1].len++;
}
status = i2c_transfer(adapter, msg, num); // 发送消息
if (status < 0)
return status; // 发送失败,返回错误
/* 如果最后一个消息是读,则检查PEC */
if (i && (msg[num-1].flags & I2C_M_RD)) {
status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
if (status < 0)
return status;
}
// 根据读写类型进行操作
if (read_write == I2C_SMBUS_READ)
switch (size) {
case I2C_SMBUS_BYTE:
data->byte = msgbuf0[0]; // 将缓冲区中的数据存入data中
break;
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[0]; // 将缓冲区中的数据存入data中
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[0] | (msgbuf1[1] << 8); // 将缓冲区中的数据存入data中
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < data->block[0]; i++)
data->block[i+1] = msgbuf1[i]; // 将缓冲区中的数据存入data中
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
for (i = 0; i < msgbuf1[0] + 1; i++)
data->block[i] = msgbuf1[i]; // 将缓冲区中的数据存入data中
break;
}
return 0;
}
i2c_transfer
ov2640_s_register->i2c_smbus_write_byte_data->i2c_smbus_xfer->i2c_smbus_xfer_emulated->i2c_transfer
这段代码实现了在I2C总线上进行传输的函数i2c_transfer。它是一个通用的I2C传输函数,用于向I2C设备发送一系列消息,并返回传输的结果。
函数首先检查适配器是否支持master_xfer函数,该函数用于执行实际的传输操作。如果适配器支持该函数,将进行以下步骤:
如果当前在原子上下文中或者中断被禁止,函数尝试获取适配器的锁定。如果无法获取锁定,表示有其他I2C活动正在进行中,函数返回-EAGAIN表示稍后重试。
如果当前不在原子上下文中且中断未被禁止,函数获取适配器的锁定。
调用__i2c_transfer函数执行实际的传输操作,传递适配器、消息数组和消息数量作为参数。
释放适配器的锁定。
返回传输的结果。
如果适配器不支持master_xfer函数,则函数打印一条调试信息并返回-EOPNOTSUPP表示不支持I2C级别的传输操作。
需要注意的是,这段代码中存在一些注释提到了错误报告模型的弱点,包括在从设备接收字节后出现错误时无法报告接收到的字节数量,以及在向从设备传输字节后收到NAK时无法报告已传输的字节数量。这些弱点需要在实际使用中进行进一步的考虑和处理。
总体而言,这段代码提供了一个通用的I2C传输函数,可以在适配器支持master_xfer函数的情况下进行传输操作,并返回传输的结果。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
/* REVISIT the fault reporting model here is weak:
*
* - 当我们从从设备接收N个字节后出现错误时,没有办法报告“N”。
*
* - 当我们向从设备传输N个字节后收到NAK时,没有办法报告“N”...或者让主设备继续执行此组合消息的其余部分,如果这是适当的响应。
*
* - 例如,“num”为2,我们成功完成第一个消息,但在第二个消息的部分中出现错误,不清楚是否应将其报告为一个(丢弃第二个消息的状态)或errno(丢弃第一个消息的状态)。
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C活动正在进行中。 */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_adapter(adap);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
__i2c_transfer
ov2640_s_register->i2c_smbus_write_byte_data->i2c_smbus_xfer->i2c_smbus_xfer_emulated->i2c_transfer->__i2c_transfer
这个函数用于在给定的I2C适配器上执行一系列I2C消息的传输操作。
函数的主要步骤如下:
检查适配器的特殊特性:如果适配器具有特殊的quirks(特性),则调用i2c_check_for_quirks函数检查传入的消息是否需要特殊处理。如果需要特殊处理,返回错误码-EOPNOTSUPP表示不支持。
启用跟踪:根据跟踪选项的状态,决定是否启用消息跟踪。如果启用了消息跟踪,使用trace_i2c_read和trace_i2c_write函数跟踪每个读取或写入消息的详细信息。
重试机制:在给定的重试次数范围内,循环执行适配器的master_xfer函数来执行I2C传输。如果返回值不是-EAGAIN(表示仲裁丢失),或者超过了指定的超时时间,跳出循环。
结果跟踪:根据跟踪选项的状态,决定是否启用结果跟踪。如果启用了结果跟踪,使用trace_i2c_reply和trace_i2c_result函数跟踪每个读取消息的响应以及整体传输结果。
返回结果:返回传输操作的结果。
总体而言,该函数负责管理I2C传输过程中的重试机制和结果跟踪,并调用适配器的master_xfer函数执行实际的传输操作。它还提供了消息跟踪的选项,以便在需要时记录传输的详细信息。
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP;
/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
* enabled. This is an efficient way of keeping the for-loop from
* being executed when not needed.
*/
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_read(adap, &msgs[i], i);
else
trace_i2c_write(adap, &msgs[i], i);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, &msgs[i], i);
trace_i2c_result(adap, i, ret);
}
return ret;
}
设置ov2640的电源ov2640_s_power
这段代码是用于设置ov2640相机设备的电源状态的函数。
函数名为ov2640_s_power,接受一个v4l2_subdev结构体指针和一个整数on作为参数。
函数的主要功能如下:
从v4l2_subdev结构体中获取i2c_client结构体指针,该结构体用于表示I2C设备。
使用i2c_client结构体指针获取soc_camera_subdev_desc结构体指针,该结构体用于描述相机子设备。
从i2c_client结构体指针中获取ov2640_priv结构体指针,该结构体是ov2640相机设备的私有数据。
调用soc_camera_set_power函数,将dev(设备)、ssdd(相机子设备描述)、priv->clk(时钟)和on(电源状态)作为参数,以设置ov2640相机设备的电源状态。
返回soc_camera_set_power函数的返回值。
该函数的作用是通过调用soc_camera_set_power函数来设置ov2640相机设备的电源状态。
// 设置ov2640的电源状态
static int ov2640_s_power(struct v4l2_subdev *sd, int on)
{
// 获取i2c_client结构体
struct i2c_client *client = v4l2_get_subdevdata(sd);
// 获取soc_camera_subdev_desc结构体
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
// 获取ov2640_priv结构体
struct ov2640_priv *priv = to_ov2640(client);
// 设置ov2640的电源状态
return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
/include/media/soc_camera.h
// 设置摄像头电源状态
static inline int soc_camera_set_power(struct device *dev,
struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
{
return on ? soc_camera_power_on(dev, ssdd, clk) // 打开电源
: soc_camera_power_off(dev, ssdd, clk); // 关闭电源
}
/driver/media/platform/soc_camera.c
这两个函数用于相机电源的开启和关闭操作。下面是对这两个函数的概括总结:
soc_camera_power_on函数用于打开相机的电源。
如果提供了时钟,并且未平衡电源或时钟状态未设置,则使能时钟。
使能寄存器。
如果存在电源回调函数,则调用电源回调函数使能相机电源。
如果上述操作都成功,则返回0。
如果有任何步骤失败,则执行相应的错误处理操作,包括禁用寄存器和时钟,并返回错误码。
soc_camera_power_off函数用于关闭相机的电源。
如果存在电源回调函数,则调用电源回调函数关闭相机电源。
禁用寄存器。
如果提供了时钟,并且未平衡电源或时钟状态已设置,则禁用时钟。
返回执行过程中的任何错误码。
这两个函数在相机驱动中用于管理相机设备的电源控制,包括使能和禁用电源和相关的时钟和寄存器。
int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
struct v4l2_clk *clk)
{
int ret;
bool clock_toggle;
if (clk && (!ssdd->unbalanced_power ||
!test_and_set_bit(0, &ssdd->clock_state))) { // 如果有时钟并且未平衡电源或时钟状态未设置
ret = v4l2_clk_enable(clk); // 使能时钟
if (ret < 0) {
dev_err(dev, "Cannot enable clock: %d\n", ret); // 不能使能时钟
return ret;
}
clock_toggle = true; // 时钟切换为真
} else {
clock_toggle = false; // 时钟切换为假
}
ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators,
ssdd->sd_pdata.regulators); // 使能寄存器
if (ret < 0) {
dev_err(dev, "Cannot enable regulators\n"); // 不能使能寄存器
goto eregenable;
}
if (ssdd->power) { // 如果有电源
ret = ssdd->power(dev, 1); // 使能电源
if (ret < 0) {
dev_err(dev,
"Platform failed to power-on the camera.\n"); // 不能使能相机电源
goto epwron;
}
}
return 0;
epwron:
regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
ssdd->sd_pdata.regulators); // 禁用寄存器
eregenable:
if (clock_toggle)
v4l2_clk_disable(clk); // 禁用时钟
return ret;
}
EXPORT_SYMBOL(soc_camera_power_on);
/**
* soc_camera_power_off() - 关闭相机电源
* @dev: 设备
* @ssdd: 相机子设备描述
* @clk: 时钟
* @return: 返回值
*/
int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
struct v4l2_clk *clk)
{
int ret = 0;
int err;
if (ssdd->power) { // 如果有电源
err = ssdd->power(dev, 0); // 关闭电源
if (err < 0) {
dev_err(dev,
"Platform failed to power-off the camera.\n"); // 不能关闭相机电源
ret = err;
}
}
err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
ssdd->sd_pdata.regulators); // 禁用寄存器
if (err < 0) {
dev_err(dev, "Cannot disable regulators\n"); // 不能禁用寄存器
ret = ret ? : err;
}
if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state)))
v4l2_clk_disable(clk); // 禁用时钟
return ret;
}
如果文章对您有帮助,点赞👍支持,感谢🤝