summaryrefslogtreecommitdiff
path: root/drivers/media/pci/mgb4/mgb4_i2c.c
blob: 2697b67e290e12acf2bc21d65b1a399a2a3d6568 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2021-2023 Digiteq Automotive
 *     author: Martin Tuma <martin.tuma@digiteqautomotive.com>
 *
 * The i2c module unifies the I2C access to the serializes/deserializes. The I2C
 * chips on the GMSL module use 16b addressing, the FPDL3 chips use standard
 * 8b addressing.
 */

#include "mgb4_i2c.h"

static int read_r16(struct i2c_client *client, u16 reg, u8 *val, int len)
{
	int ret;
	u8 buf[2];
	struct i2c_msg msg[2] = {
		{
			.addr = client->addr,
			.flags = 0,
			.len = 2,
			.buf = buf,
		}, {
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = len,
			.buf = val,
		}
	};

	buf[0] = (reg >> 8) & 0xff;
	buf[1] = (reg >> 0) & 0xff;

	ret = i2c_transfer(client->adapter, msg, 2);
	if (ret < 0)
		return ret;
	else if (ret != 2)
		return -EREMOTEIO;
	else
		return 0;
}

static int write_r16(struct i2c_client *client, u16 reg, const u8 *val, int len)
{
	int ret;
	u8 buf[4];
	struct i2c_msg msg[1] = {
		{
			.addr = client->addr,
			.flags = 0,
			.len = 2 + len,
			.buf = buf,
		}
	};

	if (2 + len > sizeof(buf))
		return -EINVAL;

	buf[0] = (reg >> 8) & 0xff;
	buf[1] = (reg >> 0) & 0xff;
	memcpy(&buf[2], val, len);

	ret = i2c_transfer(client->adapter, msg, 1);
	if (ret < 0)
		return ret;
	else if (ret != 1)
		return -EREMOTEIO;
	else
		return 0;
}

int mgb4_i2c_init(struct mgb4_i2c_client *client, struct i2c_adapter *adap,
		  struct i2c_board_info const *info, int addr_size)
{
	client->client = i2c_new_client_device(adap, info);
	if (IS_ERR(client->client))
		return PTR_ERR(client->client);

	client->addr_size = addr_size;

	return 0;
}

void mgb4_i2c_free(struct mgb4_i2c_client *client)
{
	i2c_unregister_device(client->client);
}

s32 mgb4_i2c_read_byte(struct mgb4_i2c_client *client, u16 reg)
{
	int ret;
	u8 b;

	if (client->addr_size == 8)
		return i2c_smbus_read_byte_data(client->client, reg);

	ret = read_r16(client->client, reg, &b, 1);
	if (ret < 0)
		return ret;

	return (s32)b;
}

s32 mgb4_i2c_write_byte(struct mgb4_i2c_client *client, u16 reg, u8 val)
{
	if (client->addr_size == 8)
		return i2c_smbus_write_byte_data(client->client, reg, val);
	else
		return write_r16(client->client, reg, &val, 1);
}

s32 mgb4_i2c_mask_byte(struct mgb4_i2c_client *client, u16 reg, u8 mask, u8 val)
{
	s32 ret;

	if (mask != 0xFF) {
		ret = mgb4_i2c_read_byte(client, reg);
		if (ret < 0)
			return ret;
		val |= (u8)ret & ~mask;
	}

	return mgb4_i2c_write_byte(client, reg, val);
}

int mgb4_i2c_configure(struct mgb4_i2c_client *client,
		       const struct mgb4_i2c_kv *values, size_t count)
{
	size_t i;
	s32 res;

	for (i = 0; i < count; i++) {
		res = mgb4_i2c_mask_byte(client, values[i].reg, values[i].mask,
					 values[i].val);
		if (res < 0)
			return res;
	}

	return 0;
}