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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017-2020 Jacopo Mondi
* Copyright (C) 2017-2020 Kieran Bingham
* Copyright (C) 2017-2020 Laurent Pinchart
* Copyright (C) 2017-2020 Niklas Söderlund
* Copyright (C) 2016 Renesas Electronics Corporation
* Copyright (C) 2015 Cogent Embedded, Inc.
*
* This file exports functions to control the Maxim MAX9271 GMSL serializer
* chip. This is not a self-contained driver, as MAX9271 is usually embedded in
* camera modules with at least one image sensor and optional additional
* components, such as uController units or ISPs/DSPs.
*
* Drivers for the camera modules (i.e. rdacm20/21) are expected to use
* functions exported from this library driver to maximize code re-use.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include "max9271.h"
static int max9271_read(struct max9271_device *dev, u8 reg)
{
int ret;
dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
ret = i2c_smbus_read_byte_data(dev->client, reg);
if (ret < 0)
dev_dbg(&dev->client->dev,
"%s: register 0x%02x read failed (%d)\n",
__func__, reg, ret);
return ret;
}
static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
{
int ret;
dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
ret = i2c_smbus_write_byte_data(dev->client, reg, val);
if (ret < 0)
dev_err(&dev->client->dev,
"%s: register 0x%02x write failed (%d)\n",
__func__, reg, ret);
return ret;
}
/*
* max9271_pclk_detect() - Detect valid pixel clock from image sensor
*
* Wait up to 10ms for a valid pixel clock.
*
* Returns 0 for success, < 0 for pixel clock not properly detected
*/
static int max9271_pclk_detect(struct max9271_device *dev)
{
unsigned int i;
int ret;
for (i = 0; i < 100; i++) {
ret = max9271_read(dev, 0x15);
if (ret < 0)
return ret;
if (ret & MAX9271_PCLKDET)
return 0;
usleep_range(50, 100);
}
dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
return -EIO;
}
int max9271_set_serial_link(struct max9271_device *dev, bool enable)
{
int ret;
u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
if (enable) {
ret = max9271_pclk_detect(dev);
if (ret)
return ret;
val |= MAX9271_SEREN;
} else {
val |= MAX9271_CLINKEN;
}
/*
* The serializer temporarily disables the reverse control channel for
* 350µs after starting/stopping the forward serial link, but the
* deserializer synchronization time isn't clearly documented.
*
* According to the serializer datasheet we should wait 3ms, while
* according to the deserializer datasheet we should wait 5ms.
*
* Short delays here appear to show bit-errors in the writes following.
* Therefore a conservative delay seems best here.
*/
max9271_write(dev, 0x04, val);
usleep_range(5000, 8000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_set_serial_link);
int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
{
int ret;
ret = max9271_write(dev, 0x0d, i2c_config);
if (ret)
return ret;
/* The delay required after an I2C bus configuration change is not
* characterized in the serializer manual. Sleep up to 5msec to
* stay safe.
*/
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_configure_i2c);
int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
{
int ret;
ret = max9271_read(dev, 0x08);
if (ret < 0)
return ret;
/*
* Enable or disable reverse channel high threshold to increase
* immunity to power supply noise.
*/
max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
usleep_range(2000, 2500);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_set_high_threshold);
int max9271_configure_gmsl_link(struct max9271_device *dev)
{
/*
* Configure the GMSL link:
*
* - Double input mode, high data rate, 24-bit mode
* - Latch input data on PCLKIN rising edge
* - Enable HS/VS encoding
* - 1-bit parity error detection
*
* TODO: Make the GMSL link configuration parametric.
*/
max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
MAX9271_EDC_1BIT_PARITY);
usleep_range(5000, 8000);
/*
* Adjust spread spectrum to +4% and auto-detect pixel clock
* and serial link rate.
*/
max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT);
usleep_range(5000, 8000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link);
int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
{
int ret;
ret = max9271_read(dev, 0x0f);
if (ret < 0)
return 0;
ret |= gpio_mask;
ret = max9271_write(dev, 0x0f, ret);
if (ret < 0) {
dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_set_gpios);
int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
{
int ret;
ret = max9271_read(dev, 0x0f);
if (ret < 0)
return 0;
ret &= ~gpio_mask;
ret = max9271_write(dev, 0x0f, ret);
if (ret < 0) {
dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_clear_gpios);
int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
{
int ret;
ret = max9271_read(dev, 0x0e);
if (ret < 0)
return 0;
/* BIT(0) reserved: GPO is always enabled. */
ret |= (gpio_mask & ~BIT(0));
ret = max9271_write(dev, 0x0e, ret);
if (ret < 0) {
dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_enable_gpios);
int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask)
{
int ret;
ret = max9271_read(dev, 0x0e);
if (ret < 0)
return 0;
/* BIT(0) reserved: GPO cannot be disabled */
ret &= ~(gpio_mask | BIT(0));
ret = max9271_write(dev, 0x0e, ret);
if (ret < 0) {
dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_disable_gpios);
int max9271_verify_id(struct max9271_device *dev)
{
int ret;
ret = max9271_read(dev, 0x1e);
if (ret < 0) {
dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
ret);
return ret;
}
if (ret != MAX9271_ID) {
dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
ret);
return -ENXIO;
}
return 0;
}
EXPORT_SYMBOL_GPL(max9271_verify_id);
int max9271_set_address(struct max9271_device *dev, u8 addr)
{
int ret;
ret = max9271_write(dev, 0x00, addr << 1);
if (ret < 0) {
dev_err(&dev->client->dev,
"MAX9271 I2C address change failed (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_set_address);
int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr)
{
int ret;
ret = max9271_write(dev, 0x01, addr << 1);
if (ret < 0) {
dev_err(&dev->client->dev,
"MAX9271 deserializer address set failed (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_set_deserializer_address);
int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest)
{
int ret;
ret = max9271_write(dev, 0x09, source << 1);
if (ret < 0) {
dev_err(&dev->client->dev,
"MAX9271 I2C translation setup failed (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
ret = max9271_write(dev, 0x0a, dest << 1);
if (ret < 0) {
dev_err(&dev->client->dev,
"MAX9271 I2C translation setup failed (%d)\n", ret);
return ret;
}
usleep_range(3500, 5000);
return 0;
}
EXPORT_SYMBOL_GPL(max9271_set_translation);
MODULE_DESCRIPTION("Maxim MAX9271 GMSL Serializer");
MODULE_AUTHOR("Jacopo Mondi");
MODULE_LICENSE("GPL v2");
|