diff options
author | Federico Vaga <federico.vaga@gmail.com> | 2012-04-12 11:39:36 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-20 10:59:22 -0400 |
commit | bca7ad1a332a0754860bdd57b258f8e9ee5eb2a5 (patch) | |
tree | 98b1864dfa7f70591b24ba7f61d9a65ce43d0dd4 /drivers/media/video/adv7180.c | |
parent | f27b853ea24a9b70585f9251384d97929e6551c3 (diff) |
[media] adv7180: add support to user controls
Video user controls such as brightness, contrast, saturation, and
hue are now handled.
Signed-off-by: Federico Vaga <federico.vaga@gmail.com>
Acked-by: Giancarlo Asnaghi <giancarlo.asnaghi@st.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Richard Röjfors <richard.rojfors.ext@mocean-labs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/adv7180.c')
-rw-r--r-- | drivers/media/video/adv7180.c | 417 |
1 files changed, 350 insertions, 67 deletions
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index b8b6c4b0cad4..174bffacf117 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 | 48 | #define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 |
49 | #define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 | 49 | #define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 |
50 | #define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 | 50 | #define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 |
51 | #define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f | ||
51 | 52 | ||
52 | #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 | 53 | #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 |
53 | #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 | 54 | #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 |
@@ -55,9 +56,29 @@ | |||
55 | #define ADV7180_AUTODETECT_ENABLE_REG 0x07 | 56 | #define ADV7180_AUTODETECT_ENABLE_REG 0x07 |
56 | #define ADV7180_AUTODETECT_DEFAULT 0x7f | 57 | #define ADV7180_AUTODETECT_DEFAULT 0x7f |
57 | 58 | ||
59 | #define ADV7180_CON_REG 0x08 /*Unsigned */ | ||
60 | #define CON_REG_MIN 0 | ||
61 | #define CON_REG_DEF 128 | ||
62 | #define CON_REG_MAX 255 | ||
63 | |||
64 | #define ADV7180_BRI_REG 0x0a /*Signed */ | ||
65 | #define BRI_REG_MIN -128 | ||
66 | #define BRI_REG_DEF 0 | ||
67 | #define BRI_REG_MAX 127 | ||
68 | |||
69 | #define ADV7180_HUE_REG 0x0b /*Signed, inverted */ | ||
70 | #define HUE_REG_MIN -127 | ||
71 | #define HUE_REG_DEF 0 | ||
72 | #define HUE_REG_MAX 128 | ||
73 | |||
58 | #define ADV7180_ADI_CTRL_REG 0x0e | 74 | #define ADV7180_ADI_CTRL_REG 0x0e |
59 | #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 | 75 | #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 |
60 | 76 | ||
77 | #define ADV7180_PWR_MAN_REG 0x0f | ||
78 | #define ADV7180_PWR_MAN_ON 0x04 | ||
79 | #define ADV7180_PWR_MAN_OFF 0x24 | ||
80 | #define ADV7180_PWR_MAN_RES 0x80 | ||
81 | |||
61 | #define ADV7180_STATUS1_REG 0x10 | 82 | #define ADV7180_STATUS1_REG 0x10 |
62 | #define ADV7180_STATUS1_IN_LOCK 0x01 | 83 | #define ADV7180_STATUS1_IN_LOCK 0x01 |
63 | #define ADV7180_STATUS1_AUTOD_MASK 0x70 | 84 | #define ADV7180_STATUS1_AUTOD_MASK 0x70 |
@@ -78,6 +99,12 @@ | |||
78 | #define ADV7180_ICONF1_PSYNC_ONLY 0x10 | 99 | #define ADV7180_ICONF1_PSYNC_ONLY 0x10 |
79 | #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 | 100 | #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 |
80 | 101 | ||
102 | #define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ | ||
103 | #define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ | ||
104 | #define SAT_REG_MIN 0 | ||
105 | #define SAT_REG_DEF 128 | ||
106 | #define SAT_REG_MAX 255 | ||
107 | |||
81 | #define ADV7180_IRQ1_LOCK 0x01 | 108 | #define ADV7180_IRQ1_LOCK 0x01 |
82 | #define ADV7180_IRQ1_UNLOCK 0x02 | 109 | #define ADV7180_IRQ1_UNLOCK 0x02 |
83 | #define ADV7180_ISR1_ADI 0x42 | 110 | #define ADV7180_ISR1_ADI 0x42 |
@@ -90,6 +117,9 @@ | |||
90 | #define ADV7180_IMR3_ADI 0x4C | 117 | #define ADV7180_IMR3_ADI 0x4C |
91 | #define ADV7180_IMR4_ADI 0x50 | 118 | #define ADV7180_IMR4_ADI 0x50 |
92 | 119 | ||
120 | #define ADV7180_NTSC_V_BIT_END_REG 0xE6 | ||
121 | #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F | ||
122 | |||
93 | struct adv7180_state { | 123 | struct adv7180_state { |
94 | struct v4l2_subdev sd; | 124 | struct v4l2_subdev sd; |
95 | struct work_struct work; | 125 | struct work_struct work; |
@@ -97,6 +127,11 @@ struct adv7180_state { | |||
97 | int irq; | 127 | int irq; |
98 | v4l2_std_id curr_norm; | 128 | v4l2_std_id curr_norm; |
99 | bool autodetect; | 129 | bool autodetect; |
130 | s8 brightness; | ||
131 | s16 hue; | ||
132 | u8 contrast; | ||
133 | u8 saturation; | ||
134 | u8 input; | ||
100 | }; | 135 | }; |
101 | 136 | ||
102 | static v4l2_std_id adv7180_std_to_v4l2(u8 status1) | 137 | static v4l2_std_id adv7180_std_to_v4l2(u8 status1) |
@@ -155,7 +190,7 @@ static u32 adv7180_status_to_v4l2(u8 status1) | |||
155 | } | 190 | } |
156 | 191 | ||
157 | static int __adv7180_status(struct i2c_client *client, u32 *status, | 192 | static int __adv7180_status(struct i2c_client *client, u32 *status, |
158 | v4l2_std_id *std) | 193 | v4l2_std_id *std) |
159 | { | 194 | { |
160 | int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); | 195 | int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); |
161 | 196 | ||
@@ -192,6 +227,36 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) | |||
192 | return err; | 227 | return err; |
193 | } | 228 | } |
194 | 229 | ||
230 | static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, | ||
231 | u32 output, u32 config) | ||
232 | { | ||
233 | struct adv7180_state *state = to_state(sd); | ||
234 | int ret = mutex_lock_interruptible(&state->mutex); | ||
235 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
236 | |||
237 | if (ret) | ||
238 | return ret; | ||
239 | |||
240 | /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept | ||
241 | * all inputs and let the card driver take care of validation | ||
242 | */ | ||
243 | if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) | ||
244 | goto out; | ||
245 | |||
246 | ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); | ||
247 | |||
248 | if (ret < 0) | ||
249 | goto out; | ||
250 | |||
251 | ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; | ||
252 | ret = i2c_smbus_write_byte_data(client, | ||
253 | ADV7180_INPUT_CONTROL_REG, ret | input); | ||
254 | state->input = input; | ||
255 | out: | ||
256 | mutex_unlock(&state->mutex); | ||
257 | return ret; | ||
258 | } | ||
259 | |||
195 | static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) | 260 | static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) |
196 | { | 261 | { |
197 | struct adv7180_state *state = to_state(sd); | 262 | struct adv7180_state *state = to_state(sd); |
@@ -205,7 +270,7 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) | |||
205 | } | 270 | } |
206 | 271 | ||
207 | static int adv7180_g_chip_ident(struct v4l2_subdev *sd, | 272 | static int adv7180_g_chip_ident(struct v4l2_subdev *sd, |
208 | struct v4l2_dbg_chip_ident *chip) | 273 | struct v4l2_dbg_chip_ident *chip) |
209 | { | 274 | { |
210 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 275 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
211 | 276 | ||
@@ -222,9 +287,10 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) | |||
222 | 287 | ||
223 | /* all standards -> autodetect */ | 288 | /* all standards -> autodetect */ |
224 | if (std == V4L2_STD_ALL) { | 289 | if (std == V4L2_STD_ALL) { |
225 | ret = i2c_smbus_write_byte_data(client, | 290 | ret = |
226 | ADV7180_INPUT_CONTROL_REG, | 291 | i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, |
227 | ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); | 292 | ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM |
293 | | state->input); | ||
228 | if (ret < 0) | 294 | if (ret < 0) |
229 | goto out; | 295 | goto out; |
230 | 296 | ||
@@ -236,7 +302,8 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) | |||
236 | goto out; | 302 | goto out; |
237 | 303 | ||
238 | ret = i2c_smbus_write_byte_data(client, | 304 | ret = i2c_smbus_write_byte_data(client, |
239 | ADV7180_INPUT_CONTROL_REG, ret); | 305 | ADV7180_INPUT_CONTROL_REG, |
306 | ret | state->input); | ||
240 | if (ret < 0) | 307 | if (ret < 0) |
241 | goto out; | 308 | goto out; |
242 | 309 | ||
@@ -249,14 +316,138 @@ out: | |||
249 | return ret; | 316 | return ret; |
250 | } | 317 | } |
251 | 318 | ||
319 | static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) | ||
320 | { | ||
321 | switch (qc->id) { | ||
322 | case V4L2_CID_BRIGHTNESS: | ||
323 | return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX, | ||
324 | 1, BRI_REG_DEF); | ||
325 | case V4L2_CID_HUE: | ||
326 | return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX, | ||
327 | 1, HUE_REG_DEF); | ||
328 | case V4L2_CID_CONTRAST: | ||
329 | return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX, | ||
330 | 1, CON_REG_DEF); | ||
331 | case V4L2_CID_SATURATION: | ||
332 | return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX, | ||
333 | 1, SAT_REG_DEF); | ||
334 | default: | ||
335 | break; | ||
336 | } | ||
337 | |||
338 | return -EINVAL; | ||
339 | } | ||
340 | |||
341 | static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
342 | { | ||
343 | struct adv7180_state *state = to_state(sd); | ||
344 | int ret = mutex_lock_interruptible(&state->mutex); | ||
345 | if (ret) | ||
346 | return ret; | ||
347 | |||
348 | switch (ctrl->id) { | ||
349 | case V4L2_CID_BRIGHTNESS: | ||
350 | ctrl->value = state->brightness; | ||
351 | break; | ||
352 | case V4L2_CID_HUE: | ||
353 | ctrl->value = state->hue; | ||
354 | break; | ||
355 | case V4L2_CID_CONTRAST: | ||
356 | ctrl->value = state->contrast; | ||
357 | break; | ||
358 | case V4L2_CID_SATURATION: | ||
359 | ctrl->value = state->saturation; | ||
360 | break; | ||
361 | default: | ||
362 | ret = -EINVAL; | ||
363 | } | ||
364 | |||
365 | mutex_unlock(&state->mutex); | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
370 | { | ||
371 | struct adv7180_state *state = to_state(sd); | ||
372 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
373 | int ret = mutex_lock_interruptible(&state->mutex); | ||
374 | if (ret) | ||
375 | return ret; | ||
376 | |||
377 | switch (ctrl->id) { | ||
378 | case V4L2_CID_BRIGHTNESS: | ||
379 | if ((ctrl->value > BRI_REG_MAX) | ||
380 | || (ctrl->value < BRI_REG_MIN)) { | ||
381 | ret = -ERANGE; | ||
382 | break; | ||
383 | } | ||
384 | state->brightness = ctrl->value; | ||
385 | ret = i2c_smbus_write_byte_data(client, | ||
386 | ADV7180_BRI_REG, | ||
387 | state->brightness); | ||
388 | break; | ||
389 | case V4L2_CID_HUE: | ||
390 | if ((ctrl->value > HUE_REG_MAX) | ||
391 | || (ctrl->value < HUE_REG_MIN)) { | ||
392 | ret = -ERANGE; | ||
393 | break; | ||
394 | } | ||
395 | state->hue = ctrl->value; | ||
396 | /*Hue is inverted according to HSL chart */ | ||
397 | ret = i2c_smbus_write_byte_data(client, | ||
398 | ADV7180_HUE_REG, -state->hue); | ||
399 | break; | ||
400 | case V4L2_CID_CONTRAST: | ||
401 | if ((ctrl->value > CON_REG_MAX) | ||
402 | || (ctrl->value < CON_REG_MIN)) { | ||
403 | ret = -ERANGE; | ||
404 | break; | ||
405 | } | ||
406 | state->contrast = ctrl->value; | ||
407 | ret = i2c_smbus_write_byte_data(client, | ||
408 | ADV7180_CON_REG, | ||
409 | state->contrast); | ||
410 | break; | ||
411 | case V4L2_CID_SATURATION: | ||
412 | if ((ctrl->value > SAT_REG_MAX) | ||
413 | || (ctrl->value < SAT_REG_MIN)) { | ||
414 | ret = -ERANGE; | ||
415 | break; | ||
416 | } | ||
417 | /* | ||
418 | *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE | ||
419 | *Let's not confuse the user, everybody understands saturation | ||
420 | */ | ||
421 | state->saturation = ctrl->value; | ||
422 | ret = i2c_smbus_write_byte_data(client, | ||
423 | ADV7180_SD_SAT_CB_REG, | ||
424 | state->saturation); | ||
425 | if (ret < 0) | ||
426 | break; | ||
427 | ret = i2c_smbus_write_byte_data(client, | ||
428 | ADV7180_SD_SAT_CR_REG, | ||
429 | state->saturation); | ||
430 | break; | ||
431 | default: | ||
432 | ret = -EINVAL; | ||
433 | } | ||
434 | |||
435 | mutex_unlock(&state->mutex); | ||
436 | return ret; | ||
437 | } | ||
438 | |||
252 | static const struct v4l2_subdev_video_ops adv7180_video_ops = { | 439 | static const struct v4l2_subdev_video_ops adv7180_video_ops = { |
253 | .querystd = adv7180_querystd, | 440 | .querystd = adv7180_querystd, |
254 | .g_input_status = adv7180_g_input_status, | 441 | .g_input_status = adv7180_g_input_status, |
442 | .s_routing = adv7180_s_routing, | ||
255 | }; | 443 | }; |
256 | 444 | ||
257 | static const struct v4l2_subdev_core_ops adv7180_core_ops = { | 445 | static const struct v4l2_subdev_core_ops adv7180_core_ops = { |
258 | .g_chip_ident = adv7180_g_chip_ident, | 446 | .g_chip_ident = adv7180_g_chip_ident, |
259 | .s_std = adv7180_s_std, | 447 | .s_std = adv7180_s_std, |
448 | .queryctrl = adv7180_queryctrl, | ||
449 | .g_ctrl = adv7180_g_ctrl, | ||
450 | .s_ctrl = adv7180_s_ctrl, | ||
260 | }; | 451 | }; |
261 | 452 | ||
262 | static const struct v4l2_subdev_ops adv7180_ops = { | 453 | static const struct v4l2_subdev_ops adv7180_ops = { |
@@ -267,13 +458,13 @@ static const struct v4l2_subdev_ops adv7180_ops = { | |||
267 | static void adv7180_work(struct work_struct *work) | 458 | static void adv7180_work(struct work_struct *work) |
268 | { | 459 | { |
269 | struct adv7180_state *state = container_of(work, struct adv7180_state, | 460 | struct adv7180_state *state = container_of(work, struct adv7180_state, |
270 | work); | 461 | work); |
271 | struct i2c_client *client = v4l2_get_subdevdata(&state->sd); | 462 | struct i2c_client *client = v4l2_get_subdevdata(&state->sd); |
272 | u8 isr3; | 463 | u8 isr3; |
273 | 464 | ||
274 | mutex_lock(&state->mutex); | 465 | mutex_lock(&state->mutex); |
275 | i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, | 466 | i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, |
276 | ADV7180_ADI_CTRL_IRQ_SPACE); | 467 | ADV7180_ADI_CTRL_IRQ_SPACE); |
277 | isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); | 468 | isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); |
278 | /* clear */ | 469 | /* clear */ |
279 | i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); | 470 | i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); |
@@ -297,56 +488,51 @@ static irqreturn_t adv7180_irq(int irq, void *devid) | |||
297 | return IRQ_HANDLED; | 488 | return IRQ_HANDLED; |
298 | } | 489 | } |
299 | 490 | ||
300 | /* | 491 | static int init_device(struct i2c_client *client, struct adv7180_state *state) |
301 | * Generic i2c probe | ||
302 | * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | ||
303 | */ | ||
304 | |||
305 | static __devinit int adv7180_probe(struct i2c_client *client, | ||
306 | const struct i2c_device_id *id) | ||
307 | { | 492 | { |
308 | struct adv7180_state *state; | ||
309 | struct v4l2_subdev *sd; | ||
310 | int ret; | 493 | int ret; |
311 | 494 | ||
312 | /* Check if the adapter supports the needed features */ | ||
313 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
314 | return -EIO; | ||
315 | |||
316 | v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
317 | client->addr << 1, client->adapter->name); | ||
318 | |||
319 | state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); | ||
320 | if (state == NULL) { | ||
321 | ret = -ENOMEM; | ||
322 | goto err; | ||
323 | } | ||
324 | |||
325 | state->irq = client->irq; | ||
326 | INIT_WORK(&state->work, adv7180_work); | ||
327 | mutex_init(&state->mutex); | ||
328 | state->autodetect = true; | ||
329 | sd = &state->sd; | ||
330 | v4l2_i2c_subdev_init(sd, client, &adv7180_ops); | ||
331 | |||
332 | /* Initialize adv7180 */ | 495 | /* Initialize adv7180 */ |
333 | /* Enable autodetection */ | 496 | /* Enable autodetection */ |
334 | ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, | 497 | if (state->autodetect) { |
335 | ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); | 498 | ret = |
336 | if (ret < 0) | 499 | i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, |
337 | goto err_unreg_subdev; | 500 | ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM |
501 | | state->input); | ||
502 | if (ret < 0) | ||
503 | return ret; | ||
338 | 504 | ||
339 | ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, | 505 | ret = |
340 | ADV7180_AUTODETECT_DEFAULT); | 506 | i2c_smbus_write_byte_data(client, |
341 | if (ret < 0) | 507 | ADV7180_AUTODETECT_ENABLE_REG, |
342 | goto err_unreg_subdev; | 508 | ADV7180_AUTODETECT_DEFAULT); |
509 | if (ret < 0) | ||
510 | return ret; | ||
511 | } else { | ||
512 | ret = v4l2_std_to_adv7180(state->curr_norm); | ||
513 | if (ret < 0) | ||
514 | return ret; | ||
343 | 515 | ||
516 | ret = | ||
517 | i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, | ||
518 | ret | state->input); | ||
519 | if (ret < 0) | ||
520 | return ret; | ||
521 | |||
522 | } | ||
344 | /* ITU-R BT.656-4 compatible */ | 523 | /* ITU-R BT.656-4 compatible */ |
345 | ret = i2c_smbus_write_byte_data(client, | 524 | ret = i2c_smbus_write_byte_data(client, |
346 | ADV7180_EXTENDED_OUTPUT_CONTROL_REG, | 525 | ADV7180_EXTENDED_OUTPUT_CONTROL_REG, |
347 | ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); | 526 | ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); |
348 | if (ret < 0) | 527 | if (ret < 0) |
349 | goto err_unreg_subdev; | 528 | return ret; |
529 | |||
530 | /* Manually set V bit end position in NTSC mode */ | ||
531 | ret = i2c_smbus_write_byte_data(client, | ||
532 | ADV7180_NTSC_V_BIT_END_REG, | ||
533 | ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); | ||
534 | if (ret < 0) | ||
535 | return ret; | ||
350 | 536 | ||
351 | /* read current norm */ | 537 | /* read current norm */ |
352 | __adv7180_status(client, NULL, &state->curr_norm); | 538 | __adv7180_status(client, NULL, &state->curr_norm); |
@@ -354,45 +540,109 @@ static __devinit int adv7180_probe(struct i2c_client *client, | |||
354 | /* register for interrupts */ | 540 | /* register for interrupts */ |
355 | if (state->irq > 0) { | 541 | if (state->irq > 0) { |
356 | ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, | 542 | ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, |
357 | state); | 543 | state); |
358 | if (ret) | 544 | if (ret) |
359 | goto err_unreg_subdev; | 545 | return ret; |
360 | 546 | ||
361 | ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, | 547 | ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, |
362 | ADV7180_ADI_CTRL_IRQ_SPACE); | 548 | ADV7180_ADI_CTRL_IRQ_SPACE); |
363 | if (ret < 0) | 549 | if (ret < 0) |
364 | goto err_unreg_subdev; | 550 | return ret; |
365 | 551 | ||
366 | /* config the Interrupt pin to be active low */ | 552 | /* config the Interrupt pin to be active low */ |
367 | ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, | 553 | ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, |
368 | ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); | 554 | ADV7180_ICONF1_ACTIVE_LOW | |
555 | ADV7180_ICONF1_PSYNC_ONLY); | ||
369 | if (ret < 0) | 556 | if (ret < 0) |
370 | goto err_unreg_subdev; | 557 | return ret; |
371 | 558 | ||
372 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); | 559 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); |
373 | if (ret < 0) | 560 | if (ret < 0) |
374 | goto err_unreg_subdev; | 561 | return ret; |
375 | 562 | ||
376 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); | 563 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); |
377 | if (ret < 0) | 564 | if (ret < 0) |
378 | goto err_unreg_subdev; | 565 | return ret; |
379 | 566 | ||
380 | /* enable AD change interrupts interrupts */ | 567 | /* enable AD change interrupts interrupts */ |
381 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, | 568 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, |
382 | ADV7180_IRQ3_AD_CHANGE); | 569 | ADV7180_IRQ3_AD_CHANGE); |
383 | if (ret < 0) | 570 | if (ret < 0) |
384 | goto err_unreg_subdev; | 571 | return ret; |
385 | 572 | ||
386 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); | 573 | ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); |
387 | if (ret < 0) | 574 | if (ret < 0) |
388 | goto err_unreg_subdev; | 575 | return ret; |
389 | 576 | ||
390 | ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, | 577 | ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, |
391 | 0); | 578 | 0); |
392 | if (ret < 0) | 579 | if (ret < 0) |
393 | goto err_unreg_subdev; | 580 | return ret; |
394 | } | 581 | } |
395 | 582 | ||
583 | /*Set default value for controls */ | ||
584 | ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, | ||
585 | state->brightness); | ||
586 | if (ret < 0) | ||
587 | return ret; | ||
588 | |||
589 | ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue); | ||
590 | if (ret < 0) | ||
591 | return ret; | ||
592 | |||
593 | ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, | ||
594 | state->contrast); | ||
595 | if (ret < 0) | ||
596 | return ret; | ||
597 | |||
598 | ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, | ||
599 | state->saturation); | ||
600 | if (ret < 0) | ||
601 | return ret; | ||
602 | |||
603 | ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, | ||
604 | state->saturation); | ||
605 | if (ret < 0) | ||
606 | return ret; | ||
607 | |||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | static __devinit int adv7180_probe(struct i2c_client *client, | ||
612 | const struct i2c_device_id *id) | ||
613 | { | ||
614 | struct adv7180_state *state; | ||
615 | struct v4l2_subdev *sd; | ||
616 | int ret; | ||
617 | |||
618 | /* Check if the adapter supports the needed features */ | ||
619 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
620 | return -EIO; | ||
621 | |||
622 | v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
623 | client->addr, client->adapter->name); | ||
624 | |||
625 | state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); | ||
626 | if (state == NULL) { | ||
627 | ret = -ENOMEM; | ||
628 | goto err; | ||
629 | } | ||
630 | |||
631 | state->irq = client->irq; | ||
632 | INIT_WORK(&state->work, adv7180_work); | ||
633 | mutex_init(&state->mutex); | ||
634 | state->autodetect = true; | ||
635 | state->brightness = BRI_REG_DEF; | ||
636 | state->hue = HUE_REG_DEF; | ||
637 | state->contrast = CON_REG_DEF; | ||
638 | state->saturation = SAT_REG_DEF; | ||
639 | state->input = 0; | ||
640 | sd = &state->sd; | ||
641 | v4l2_i2c_subdev_init(sd, client, &adv7180_ops); | ||
642 | |||
643 | ret = init_device(client, state); | ||
644 | if (0 != ret) | ||
645 | goto err_unreg_subdev; | ||
396 | return 0; | 646 | return 0; |
397 | 647 | ||
398 | err_unreg_subdev: | 648 | err_unreg_subdev: |
@@ -432,16 +682,49 @@ static const struct i2c_device_id adv7180_id[] = { | |||
432 | {}, | 682 | {}, |
433 | }; | 683 | }; |
434 | 684 | ||
685 | #ifdef CONFIG_PM | ||
686 | static int adv7180_suspend(struct i2c_client *client, pm_message_t state) | ||
687 | { | ||
688 | int ret; | ||
689 | |||
690 | ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, | ||
691 | ADV7180_PWR_MAN_OFF); | ||
692 | if (ret < 0) | ||
693 | return ret; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int adv7180_resume(struct i2c_client *client) | ||
698 | { | ||
699 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
700 | struct adv7180_state *state = to_state(sd); | ||
701 | int ret; | ||
702 | |||
703 | ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, | ||
704 | ADV7180_PWR_MAN_ON); | ||
705 | if (ret < 0) | ||
706 | return ret; | ||
707 | ret = init_device(client, state); | ||
708 | if (ret < 0) | ||
709 | return ret; | ||
710 | return 0; | ||
711 | } | ||
712 | #endif | ||
713 | |||
435 | MODULE_DEVICE_TABLE(i2c, adv7180_id); | 714 | MODULE_DEVICE_TABLE(i2c, adv7180_id); |
436 | 715 | ||
437 | static struct i2c_driver adv7180_driver = { | 716 | static struct i2c_driver adv7180_driver = { |
438 | .driver = { | 717 | .driver = { |
439 | .owner = THIS_MODULE, | 718 | .owner = THIS_MODULE, |
440 | .name = DRIVER_NAME, | 719 | .name = DRIVER_NAME, |
441 | }, | 720 | }, |
442 | .probe = adv7180_probe, | 721 | .probe = adv7180_probe, |
443 | .remove = __devexit_p(adv7180_remove), | 722 | .remove = __devexit_p(adv7180_remove), |
444 | .id_table = adv7180_id, | 723 | #ifdef CONFIG_PM |
724 | .suspend = adv7180_suspend, | ||
725 | .resume = adv7180_resume, | ||
726 | #endif | ||
727 | .id_table = adv7180_id, | ||
445 | }; | 728 | }; |
446 | 729 | ||
447 | module_i2c_driver(adv7180_driver); | 730 | module_i2c_driver(adv7180_driver); |