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#include <linux/module.h>
32#include <linux/types.h>
33#include <linux/slab.h>
34#include <linux/ioctl.h>
35#include <asm/uaccess.h>
36#include <linux/i2c.h>
37#include <linux/videodev2.h>
38#include <media/v4l2-device.h>
39
40MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
41MODULE_AUTHOR("Maxim Yevtyushkin");
42MODULE_LICENSE("GPL");
43
44
45static int debug;
46module_param(debug, int, 0);
47MODULE_PARM_DESC(debug, "Debug level (0-1)");
48
49
50
51struct adv7170 {
52 struct v4l2_subdev sd;
53 unsigned char reg[128];
54
55 v4l2_std_id norm;
56 int input;
57};
58
59static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd)
60{
61 return container_of(sd, struct adv7170, sd);
62}
63
64static char *inputs[] = { "pass_through", "play_back" };
65
66static enum v4l2_mbus_pixelcode adv7170_codes[] = {
67 V4L2_MBUS_FMT_UYVY8_2X8,
68 V4L2_MBUS_FMT_UYVY8_1X16,
69};
70
71
72
73static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value)
74{
75 struct i2c_client *client = v4l2_get_subdevdata(sd);
76 struct adv7170 *encoder = to_adv7170(sd);
77
78 encoder->reg[reg] = value;
79 return i2c_smbus_write_byte_data(client, reg, value);
80}
81
82static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg)
83{
84 struct i2c_client *client = v4l2_get_subdevdata(sd);
85
86 return i2c_smbus_read_byte_data(client, reg);
87}
88
89static int adv7170_write_block(struct v4l2_subdev *sd,
90 const u8 *data, unsigned int len)
91{
92 struct i2c_client *client = v4l2_get_subdevdata(sd);
93 struct adv7170 *encoder = to_adv7170(sd);
94 int ret = -1;
95 u8 reg;
96
97
98
99 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
100
101 u8 block_data[32];
102 int block_len;
103
104 while (len >= 2) {
105 block_len = 0;
106 block_data[block_len++] = reg = data[0];
107 do {
108 block_data[block_len++] =
109 encoder->reg[reg++] = data[1];
110 len -= 2;
111 data += 2;
112 } while (len >= 2 && data[0] == reg && block_len < 32);
113 ret = i2c_master_send(client, block_data, block_len);
114 if (ret < 0)
115 break;
116 }
117 } else {
118
119 while (len >= 2) {
120 reg = *data++;
121 ret = adv7170_write(sd, reg, *data++);
122 if (ret < 0)
123 break;
124 len -= 2;
125 }
126 }
127 return ret;
128}
129
130
131
132#define TR0MODE 0x4c
133#define TR0RST 0x80
134
135#define TR1CAPT 0x00
136#define TR1PLAY 0x00
137
138static const unsigned char init_NTSC[] = {
139 0x00, 0x10,
140 0x01, 0x20,
141 0x02, 0x0e,
142 0x03, 0x80,
143 0x04, 0x30,
144 0x05, 0x00,
145 0x06, 0x00,
146 0x07, TR0MODE,
147 0x08, TR1CAPT,
148 0x09, 0x16,
149 0x0a, 0x7c,
150 0x0b, 0xf0,
151 0x0c, 0x21,
152 0x0d, 0x00,
153 0x0e, 0x00,
154 0x0f, 0x00,
155 0x10, 0x00,
156 0x11, 0x00,
157 0x12, 0x00,
158 0x13, 0x00,
159 0x14, 0x00,
160 0x15, 0x00,
161 0x16, 0x00,
162 0x17, 0x00,
163 0x18, 0x00,
164 0x19, 0x00,
165};
166
167static const unsigned char init_PAL[] = {
168 0x00, 0x71,
169 0x01, 0x20,
170 0x02, 0x0e,
171 0x03, 0x80,
172 0x04, 0x30,
173 0x05, 0x00,
174 0x06, 0x00,
175 0x07, TR0MODE,
176 0x08, TR1CAPT,
177 0x09, 0xcb,
178 0x0a, 0x8a,
179 0x0b, 0x09,
180 0x0c, 0x2a,
181 0x0d, 0x00,
182 0x0e, 0x00,
183 0x0f, 0x00,
184 0x10, 0x00,
185 0x11, 0x00,
186 0x12, 0x00,
187 0x13, 0x00,
188 0x14, 0x00,
189 0x15, 0x00,
190 0x16, 0x00,
191 0x17, 0x00,
192 0x18, 0x00,
193 0x19, 0x00,
194};
195
196
197static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
198{
199 struct adv7170 *encoder = to_adv7170(sd);
200
201 v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
202
203 if (std & V4L2_STD_NTSC) {
204 adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
205 if (encoder->input == 0)
206 adv7170_write(sd, 0x02, 0x0e);
207 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
208 adv7170_write(sd, 0x07, TR0MODE);
209 } else if (std & V4L2_STD_PAL) {
210 adv7170_write_block(sd, init_PAL, sizeof(init_PAL));
211 if (encoder->input == 0)
212 adv7170_write(sd, 0x02, 0x0e);
213 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
214 adv7170_write(sd, 0x07, TR0MODE);
215 } else {
216 v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
217 (unsigned long long)std);
218 return -EINVAL;
219 }
220 v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
221 encoder->norm = std;
222 return 0;
223}
224
225static int adv7170_s_routing(struct v4l2_subdev *sd,
226 u32 input, u32 output, u32 config)
227{
228 struct adv7170 *encoder = to_adv7170(sd);
229
230
231
232
233
234 v4l2_dbg(1, debug, sd, "set input from %s\n",
235 input == 0 ? "decoder" : "ZR36060");
236
237 switch (input) {
238 case 0:
239 adv7170_write(sd, 0x01, 0x20);
240 adv7170_write(sd, 0x08, TR1CAPT);
241 adv7170_write(sd, 0x02, 0x0e);
242 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
243 adv7170_write(sd, 0x07, TR0MODE);
244
245 break;
246
247 case 1:
248 adv7170_write(sd, 0x01, 0x00);
249 adv7170_write(sd, 0x08, TR1PLAY);
250 adv7170_write(sd, 0x02, 0x08);
251 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
252 adv7170_write(sd, 0x07, TR0MODE);
253
254 break;
255
256 default:
257 v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
258 return -EINVAL;
259 }
260 v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
261 encoder->input = input;
262 return 0;
263}
264
265static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
266 enum v4l2_mbus_pixelcode *code)
267{
268 if (index >= ARRAY_SIZE(adv7170_codes))
269 return -EINVAL;
270
271 *code = adv7170_codes[index];
272 return 0;
273}
274
275static int adv7170_g_fmt(struct v4l2_subdev *sd,
276 struct v4l2_mbus_framefmt *mf)
277{
278 u8 val = adv7170_read(sd, 0x7);
279
280 if ((val & 0x40) == (1 << 6))
281 mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
282 else
283 mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
284
285 mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
286 mf->width = 0;
287 mf->height = 0;
288 mf->field = V4L2_FIELD_ANY;
289
290 return 0;
291}
292
293static int adv7170_s_fmt(struct v4l2_subdev *sd,
294 struct v4l2_mbus_framefmt *mf)
295{
296 u8 val = adv7170_read(sd, 0x7);
297 int ret;
298
299 switch (mf->code) {
300 case V4L2_MBUS_FMT_UYVY8_2X8:
301 val &= ~0x40;
302 break;
303
304 case V4L2_MBUS_FMT_UYVY8_1X16:
305 val |= 0x40;
306 break;
307
308 default:
309 v4l2_dbg(1, debug, sd,
310 "illegal v4l2_mbus_framefmt code: %d\n", mf->code);
311 return -EINVAL;
312 }
313
314 ret = adv7170_write(sd, 0x7, val);
315
316 return ret;
317}
318
319
320
321static const struct v4l2_subdev_video_ops adv7170_video_ops = {
322 .s_std_output = adv7170_s_std_output,
323 .s_routing = adv7170_s_routing,
324 .s_mbus_fmt = adv7170_s_fmt,
325 .g_mbus_fmt = adv7170_g_fmt,
326 .enum_mbus_fmt = adv7170_enum_fmt,
327};
328
329static const struct v4l2_subdev_ops adv7170_ops = {
330 .video = &adv7170_video_ops,
331};
332
333
334
335static int adv7170_probe(struct i2c_client *client,
336 const struct i2c_device_id *id)
337{
338 struct adv7170 *encoder;
339 struct v4l2_subdev *sd;
340 int i;
341
342
343 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
344 return -ENODEV;
345
346 v4l_info(client, "chip found @ 0x%x (%s)\n",
347 client->addr << 1, client->adapter->name);
348
349 encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
350 if (encoder == NULL)
351 return -ENOMEM;
352 sd = &encoder->sd;
353 v4l2_i2c_subdev_init(sd, client, &adv7170_ops);
354 encoder->norm = V4L2_STD_NTSC;
355 encoder->input = 0;
356
357 i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
358 if (i >= 0) {
359 i = adv7170_write(sd, 0x07, TR0MODE | TR0RST);
360 i = adv7170_write(sd, 0x07, TR0MODE);
361 i = adv7170_read(sd, 0x12);
362 v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
363 }
364 if (i < 0)
365 v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
366 return 0;
367}
368
369static int adv7170_remove(struct i2c_client *client)
370{
371 struct v4l2_subdev *sd = i2c_get_clientdata(client);
372
373 v4l2_device_unregister_subdev(sd);
374 return 0;
375}
376
377
378
379static const struct i2c_device_id adv7170_id[] = {
380 { "adv7170", 0 },
381 { "adv7171", 0 },
382 { }
383};
384MODULE_DEVICE_TABLE(i2c, adv7170_id);
385
386static struct i2c_driver adv7170_driver = {
387 .driver = {
388 .owner = THIS_MODULE,
389 .name = "adv7170",
390 },
391 .probe = adv7170_probe,
392 .remove = adv7170_remove,
393 .id_table = adv7170_id,
394};
395
396module_i2c_driver(adv7170_driver);
397