Commit | Line | Data |
---|---|---|
65b39741 HG |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* | |
3 | * I2C register access helpers for Omnivision OVxxxx image sensors which expect | |
4 | * a 16 bit register address in big-endian format and which have 1-3 byte | |
5 | * wide registers, in big-endian format (for the higher width registers). | |
6 | * | |
7 | * Based on the register helpers from drivers/media/i2c/ov2680.c which is: | |
8 | * Copyright (C) 2018 Linaro Ltd | |
9 | */ | |
10 | #ifndef __OV_16BIT_ADDR_REG_HELPERS_H | |
11 | #define __OV_16BIT_ADDR_REG_HELPERS_H | |
12 | ||
13 | #include <asm/unaligned.h> | |
14 | #include <linux/dev_printk.h> | |
15 | #include <linux/i2c.h> | |
16 | ||
17 | static inline int ov_read_reg(struct i2c_client *client, u16 reg, | |
18 | unsigned int len, u32 *val) | |
19 | { | |
20 | u8 addr_buf[2], data_buf[4] = { }; | |
21 | struct i2c_msg msgs[2]; | |
22 | int ret; | |
23 | ||
24 | if (len > 4) | |
25 | return -EINVAL; | |
26 | ||
27 | put_unaligned_be16(reg, addr_buf); | |
28 | ||
29 | msgs[0].addr = client->addr; | |
30 | msgs[0].flags = 0; | |
31 | msgs[0].len = ARRAY_SIZE(addr_buf); | |
32 | msgs[0].buf = addr_buf; | |
33 | ||
34 | msgs[1].addr = client->addr; | |
35 | msgs[1].flags = I2C_M_RD; | |
36 | msgs[1].len = len; | |
37 | msgs[1].buf = &data_buf[4 - len]; | |
38 | ||
39 | ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | |
40 | if (ret != ARRAY_SIZE(msgs)) { | |
41 | dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); | |
42 | return -EIO; | |
43 | } | |
44 | ||
45 | *val = get_unaligned_be32(data_buf); | |
46 | ||
47 | return 0; | |
48 | } | |
49 | ||
50 | #define ov_read_reg8(s, r, v) ov_read_reg(s, r, 1, v) | |
51 | #define ov_read_reg16(s, r, v) ov_read_reg(s, r, 2, v) | |
52 | #define ov_read_reg24(s, r, v) ov_read_reg(s, r, 3, v) | |
53 | ||
54 | static inline int ov_write_reg(struct i2c_client *client, u16 reg, | |
55 | unsigned int len, u32 val) | |
56 | { | |
57 | u8 buf[6]; | |
58 | int ret; | |
59 | ||
60 | if (len > 4) | |
61 | return -EINVAL; | |
62 | ||
63 | put_unaligned_be16(reg, buf); | |
64 | put_unaligned_be32(val << (8 * (4 - len)), buf + 2); | |
65 | ret = i2c_master_send(client, buf, len + 2); | |
66 | if (ret != len + 2) { | |
67 | dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); | |
68 | return -EIO; | |
69 | } | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | #define ov_write_reg8(s, r, v) ov_write_reg(s, r, 1, v) | |
75 | #define ov_write_reg16(s, r, v) ov_write_reg(s, r, 2, v) | |
76 | #define ov_write_reg24(s, r, v) ov_write_reg(s, r, 3, v) | |
77 | ||
78 | static inline int ov_update_reg(struct i2c_client *client, u16 reg, u8 mask, u8 val) | |
79 | { | |
80 | u32 readval; | |
81 | int ret; | |
82 | ||
83 | ret = ov_read_reg8(client, reg, &readval); | |
84 | if (ret < 0) | |
85 | return ret; | |
86 | ||
87 | val = (readval & ~mask) | (val & mask); | |
88 | ||
89 | return ov_write_reg8(client, reg, val); | |
90 | } | |
91 | ||
92 | #endif |