Blame view
drivers/media/video/adv7170.c
9.4 KB
d56410e0a
|
1 |
/* |
1da177e4c
|
2 3 4 5 |
* adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 * * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com> * |
d56410e0a
|
6 |
* Based on adv7176 driver by: |
1da177e4c
|
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 |
* * Copyright (C) 1998 Dave Perks <dperks@ibm.net> * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net> * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> * - some corrections for Pinnacle Systems Inc. DC10plus card. * * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net> * - moved over to linux>=2.4.x i2c protocol (1/1/2003) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> |
18f3fa1e2
|
32 |
#include <linux/types.h> |
b9a21f84b
|
33 |
#include <linux/ioctl.h> |
18f3fa1e2
|
34 |
#include <asm/uaccess.h> |
b9a21f84b
|
35 36 |
#include <linux/i2c.h> #include <linux/i2c-id.h> |
7d9ef21c2
|
37 38 39 |
#include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> |
2d26698e8
|
40 |
#include <media/v4l2-i2c-drv.h> |
1da177e4c
|
41 42 43 44 |
MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); MODULE_AUTHOR("Maxim Yevtyushkin"); MODULE_LICENSE("GPL"); |
7d9ef21c2
|
45 |
|
ff699e6bd
|
46 |
static int debug; |
1da177e4c
|
47 48 |
module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); |
1da177e4c
|
49 50 51 |
/* ----------------------------------------------------------------------- */ struct adv7170 { |
7d9ef21c2
|
52 |
struct v4l2_subdev sd; |
1da177e4c
|
53 |
unsigned char reg[128]; |
107063c61
|
54 |
v4l2_std_id norm; |
1da177e4c
|
55 |
int input; |
1da177e4c
|
56 |
}; |
7d9ef21c2
|
57 58 59 60 |
static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) { return container_of(sd, struct adv7170, sd); } |
1da177e4c
|
61 |
static char *inputs[] = { "pass_through", "play_back" }; |
1da177e4c
|
62 63 |
/* ----------------------------------------------------------------------- */ |
7d9ef21c2
|
64 |
static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) |
1da177e4c
|
65 |
{ |
7d9ef21c2
|
66 67 |
struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7170 *encoder = to_adv7170(sd); |
1da177e4c
|
68 69 70 71 |
encoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } |
7d9ef21c2
|
72 |
static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) |
1da177e4c
|
73 |
{ |
7d9ef21c2
|
74 |
struct i2c_client *client = v4l2_get_subdevdata(sd); |
1da177e4c
|
75 76 |
return i2c_smbus_read_byte_data(client, reg); } |
7d9ef21c2
|
77 |
static int adv7170_write_block(struct v4l2_subdev *sd, |
b9a21f84b
|
78 |
const u8 *data, unsigned int len) |
1da177e4c
|
79 |
{ |
7d9ef21c2
|
80 81 |
struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7170 *encoder = to_adv7170(sd); |
1da177e4c
|
82 83 84 85 86 87 88 |
int ret = -1; u8 reg; /* the adv7170 has an autoincrement function, use it if * the adapter understands raw I2C */ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { /* do raw I2C, not smbus compatible */ |
1da177e4c
|
89 |
u8 block_data[32]; |
9aa45e34d
|
90 |
int block_len; |
1da177e4c
|
91 |
|
1da177e4c
|
92 |
while (len >= 2) { |
9aa45e34d
|
93 94 |
block_len = 0; block_data[block_len++] = reg = data[0]; |
1da177e4c
|
95 |
do { |
9aa45e34d
|
96 |
block_data[block_len++] = |
1da177e4c
|
97 98 99 |
encoder->reg[reg++] = data[1]; len -= 2; data += 2; |
b9a21f84b
|
100 101 102 |
} while (len >= 2 && data[0] == reg && block_len < 32); ret = i2c_master_send(client, block_data, block_len); if (ret < 0) |
1da177e4c
|
103 104 105 106 107 108 |
break; } } else { /* do some slow I2C emulation kind of thing */ while (len >= 2) { reg = *data++; |
7d9ef21c2
|
109 |
ret = adv7170_write(sd, reg, *data++); |
b9a21f84b
|
110 |
if (ret < 0) |
1da177e4c
|
111 112 113 114 |
break; len -= 2; } } |
1da177e4c
|
115 116 117 118 |
return ret; } /* ----------------------------------------------------------------------- */ |
1da177e4c
|
119 120 121 122 123 124 |
#define TR0MODE 0x4c #define TR0RST 0x80 #define TR1CAPT 0x00 #define TR1PLAY 0x00 |
1da177e4c
|
125 |
static const unsigned char init_NTSC[] = { |
7d9ef21c2
|
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 |
0x00, 0x10, /* MR0 */ 0x01, 0x20, /* MR1 */ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ 0x03, 0x80, /* MR3 */ 0x04, 0x30, /* MR4 */ 0x05, 0x00, /* Reserved */ 0x06, 0x00, /* Reserved */ 0x07, TR0MODE, /* TM0 */ 0x08, TR1CAPT, /* TM1 */ 0x09, 0x16, /* Fsc0 */ 0x0a, 0x7c, /* Fsc1 */ 0x0b, 0xf0, /* Fsc2 */ 0x0c, 0x21, /* Fsc3 */ 0x0d, 0x00, /* Subcarrier Phase */ 0x0e, 0x00, /* Closed Capt. Ext 0 */ 0x0f, 0x00, /* Closed Capt. Ext 1 */ 0x10, 0x00, /* Closed Capt. 0 */ 0x11, 0x00, /* Closed Capt. 1 */ 0x12, 0x00, /* Pedestal Ctl 0 */ 0x13, 0x00, /* Pedestal Ctl 1 */ 0x14, 0x00, /* Pedestal Ctl 2 */ 0x15, 0x00, /* Pedestal Ctl 3 */ 0x16, 0x00, /* CGMS_WSS_0 */ 0x17, 0x00, /* CGMS_WSS_1 */ 0x18, 0x00, /* CGMS_WSS_2 */ 0x19, 0x00, /* Teletext Ctl */ |
1da177e4c
|
152 153 154 |
}; static const unsigned char init_PAL[] = { |
7d9ef21c2
|
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 |
0x00, 0x71, /* MR0 */ 0x01, 0x20, /* MR1 */ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ 0x03, 0x80, /* MR3 */ 0x04, 0x30, /* MR4 */ 0x05, 0x00, /* Reserved */ 0x06, 0x00, /* Reserved */ 0x07, TR0MODE, /* TM0 */ 0x08, TR1CAPT, /* TM1 */ 0x09, 0xcb, /* Fsc0 */ 0x0a, 0x8a, /* Fsc1 */ 0x0b, 0x09, /* Fsc2 */ 0x0c, 0x2a, /* Fsc3 */ 0x0d, 0x00, /* Subcarrier Phase */ 0x0e, 0x00, /* Closed Capt. Ext 0 */ 0x0f, 0x00, /* Closed Capt. Ext 1 */ 0x10, 0x00, /* Closed Capt. 0 */ 0x11, 0x00, /* Closed Capt. 1 */ 0x12, 0x00, /* Pedestal Ctl 0 */ 0x13, 0x00, /* Pedestal Ctl 1 */ 0x14, 0x00, /* Pedestal Ctl 2 */ 0x15, 0x00, /* Pedestal Ctl 3 */ 0x16, 0x00, /* CGMS_WSS_0 */ 0x17, 0x00, /* CGMS_WSS_1 */ 0x18, 0x00, /* CGMS_WSS_2 */ 0x19, 0x00, /* Teletext Ctl */ |
1da177e4c
|
181 |
}; |
7d9ef21c2
|
182 |
static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) |
1da177e4c
|
183 |
{ |
7d9ef21c2
|
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
struct adv7170 *encoder = to_adv7170(sd); v4l2_dbg(1, debug, sd, "set norm %llx ", std); if (std & V4L2_STD_NTSC) { adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); if (encoder->input == 0) adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ adv7170_write(sd, 0x07, TR0MODE | TR0RST); adv7170_write(sd, 0x07, TR0MODE); } else if (std & V4L2_STD_PAL) { adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); if (encoder->input == 0) adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ adv7170_write(sd, 0x07, TR0MODE | TR0RST); adv7170_write(sd, 0x07, TR0MODE); } else { v4l2_dbg(1, debug, sd, "illegal norm: %llx ", std); return -EINVAL; |
b9a21f84b
|
205 |
} |
7d9ef21c2
|
206 207 208 209 210 |
v4l2_dbg(1, debug, sd, "switched to %llx ", std); encoder->norm = std; return 0; } |
1da177e4c
|
211 |
|
7d9ef21c2
|
212 213 214 |
static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { struct adv7170 *encoder = to_adv7170(sd); |
1da177e4c
|
215 |
|
7d9ef21c2
|
216 217 218 |
/* RJ: route->input = 0: input is from decoder route->input = 1: input is from ZR36060 route->input = 2: color bar */ |
1da177e4c
|
219 |
|
7d9ef21c2
|
220 221 |
v4l2_dbg(1, debug, sd, "set input from %s ", |
107063c61
|
222 |
route->input == 0 ? "decoder" : "ZR36060"); |
1da177e4c
|
223 |
|
7d9ef21c2
|
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
switch (route->input) { case 0: adv7170_write(sd, 0x01, 0x20); adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ adv7170_write(sd, 0x07, TR0MODE | TR0RST); adv7170_write(sd, 0x07, TR0MODE); /* udelay(10); */ break; case 1: adv7170_write(sd, 0x01, 0x00); adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ adv7170_write(sd, 0x02, 0x08); adv7170_write(sd, 0x07, TR0MODE | TR0RST); adv7170_write(sd, 0x07, TR0MODE); /* udelay(10); */ |
1da177e4c
|
241 242 243 |
break; default: |
7d9ef21c2
|
244 245 |
v4l2_dbg(1, debug, sd, "illegal input: %d ", route->input); |
1da177e4c
|
246 247 |
return -EINVAL; } |
7d9ef21c2
|
248 249 250 |
v4l2_dbg(1, debug, sd, "switched to %s ", inputs[route->input]); encoder->input = route->input; |
1da177e4c
|
251 252 |
return 0; } |
7d9ef21c2
|
253 254 255 |
static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); |
35631dcc7
|
256 |
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); |
7d9ef21c2
|
257 |
} |
1da177e4c
|
258 |
/* ----------------------------------------------------------------------- */ |
7d9ef21c2
|
259 260 |
static const struct v4l2_subdev_core_ops adv7170_core_ops = { .g_chip_ident = adv7170_g_chip_ident, |
1da177e4c
|
261 |
}; |
1da177e4c
|
262 |
|
7d9ef21c2
|
263 264 265 266 267 268 269 270 271 272 273 |
static const struct v4l2_subdev_video_ops adv7170_video_ops = { .s_std_output = adv7170_s_std_output, .s_routing = adv7170_s_routing, }; static const struct v4l2_subdev_ops adv7170_ops = { .core = &adv7170_core_ops, .video = &adv7170_video_ops, }; /* ----------------------------------------------------------------------- */ |
d56410e0a
|
274 |
|
b9a21f84b
|
275 276 |
static int adv7170_probe(struct i2c_client *client, const struct i2c_device_id *id) |
1da177e4c
|
277 |
{ |
1da177e4c
|
278 |
struct adv7170 *encoder; |
7d9ef21c2
|
279 |
struct v4l2_subdev *sd; |
b9a21f84b
|
280 |
int i; |
1da177e4c
|
281 282 |
/* Check if the adapter supports the needed features */ |
b9a21f84b
|
283 284 |
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; |
1da177e4c
|
285 |
|
b9a21f84b
|
286 287 288 |
v4l_info(client, "chip found @ 0x%x (%s) ", client->addr << 1, client->adapter->name); |
1da177e4c
|
289 |
|
7408187d2
|
290 |
encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); |
b9a21f84b
|
291 |
if (encoder == NULL) |
1da177e4c
|
292 |
return -ENOMEM; |
7d9ef21c2
|
293 294 |
sd = &encoder->sd; v4l2_i2c_subdev_init(sd, client, &adv7170_ops); |
107063c61
|
295 |
encoder->norm = V4L2_STD_NTSC; |
1da177e4c
|
296 |
encoder->input = 0; |
1da177e4c
|
297 |
|
7d9ef21c2
|
298 |
i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); |
1da177e4c
|
299 |
if (i >= 0) { |
7d9ef21c2
|
300 301 302 303 304 |
i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); i = adv7170_write(sd, 0x07, TR0MODE); i = adv7170_read(sd, 0x12); v4l2_dbg(1, debug, sd, "revision %d ", i & 1); |
1da177e4c
|
305 |
} |
b9a21f84b
|
306 |
if (i < 0) |
7d9ef21c2
|
307 308 |
v4l2_dbg(1, debug, sd, "init error 0x%x ", i); |
1da177e4c
|
309 310 |
return 0; } |
b9a21f84b
|
311 |
static int adv7170_remove(struct i2c_client *client) |
1da177e4c
|
312 |
{ |
7d9ef21c2
|
313 314 315 316 |
struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); kfree(to_adv7170(sd)); |
1da177e4c
|
317 318 319 320 |
return 0; } /* ----------------------------------------------------------------------- */ |
b9a21f84b
|
321 322 323 324 325 326 |
static const struct i2c_device_id adv7170_id[] = { { "adv7170", 0 }, { "adv7171", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, adv7170_id); |
1da177e4c
|
327 |
|
b9a21f84b
|
328 329 |
static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "adv7170", |
b9a21f84b
|
330 331 332 |
.probe = adv7170_probe, .remove = adv7170_remove, .id_table = adv7170_id, |
1da177e4c
|
333 |
}; |