From patchwork Thu Apr 12 16:39:36 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Federico Vaga X-Patchwork-Id: 10658 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1SIN28-0000Hr-W1 for patchwork@linuxtv.org; Thu, 12 Apr 2012 18:38:09 +0200 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.75/mailfrontend-2) with esmtp for id 1SIN27-0000V0-Ho; Thu, 12 Apr 2012 18:38:08 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965564Ab2DLQgs (ORCPT ); Thu, 12 Apr 2012 12:36:48 -0400 Received: from mail-bk0-f46.google.com ([209.85.214.46]:39343 "EHLO mail-bk0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934492Ab2DLQgr (ORCPT ); Thu, 12 Apr 2012 12:36:47 -0400 Received: by bkcik5 with SMTP id ik5so1676046bkc.19 for ; Thu, 12 Apr 2012 09:36:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer; bh=vJk6cIR0lB9cR2OtQ2T646nJgv61d5RR75ZtGGI6yM0=; b=d9uy0MyCs6vfNsd1fI9hm/G2nizd0s3OcQBlU2bAWsMxIPHQfULLORyZDdm9UVvi0U FJriwuBDXpCiv6e/H79TH69UBy/mjX39/nQFqIjEoPs6Kz2Z4lUfFj8OQqW/lgN6JHLl opYdriI4GRlp+hlWAijNOg/pybREYCfVDw3gGKMis2VvHbtGQqST0+RU22kGMRajPfMd 6LdopDB0fxAgYWRdfhjZqxtu9Lq2UKPVVhluvke3zFib+N2JvLfDsYi5fKamS13R3mMT fbEoJjB/LKTRUdI3PZm9s2trmD6KHIaUcT7qhWg0DSYYdS2Zd6bdsfog1KwelHUXL1bb KPAw== Received: by 10.204.133.196 with SMTP id g4mr999977bkt.0.1334248605161; Thu, 12 Apr 2012 09:36:45 -0700 (PDT) Received: from harkonnen.fastwebnet.it (2-225-41-194.ip174.fastwebnet.it. [2.225.41.194]) by mx.google.com with ESMTPS id f11sm11977174bkw.6.2012.04.12.09.36.43 (version=SSLv3 cipher=OTHER); Thu, 12 Apr 2012 09:36:44 -0700 (PDT) From: Federico Vaga To: Mauro Carvalho Chehab Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, Alan Cox , Giancarlo Asnaghi , Federico Vaga Subject: [PATCH 1/3] adv7180: add support to user controls Date: Thu, 12 Apr 2012 18:39:36 +0200 Message-Id: <1334248778-16625-1-git-send-email-federico.vaga@gmail.com> X-Mailer: git-send-email 1.7.7.6 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 5.6.1.2065439, Antispam-Engine: 2.7.2.376379, Antispam-Data: 2012.4.12.162732 X-PMX-Spam: Gauge=IIIIIIIII, Probability=9%, Report=' FORGED_FROM_GMAIL 0.1, MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, ECARD_WORD 0, __ANY_URI 0, __CP_MEDIA_BODY 0, __FRAUD_BODY_WEBMAIL 0, __FRAUD_WEBMAIL 0, __FRAUD_WEBMAIL_FROM 0, __FROM_GMAIL 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __PHISH_SPEAR_STRUCTURE_1 0, __SANE_MSGID 0, __STOCK_PHRASE_7 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __URI_NO_PATH 0, __URI_NO_WWW 0, __URI_NS ' Video user controls such as brightness, contrast, saturation, and hue are now handled. Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Cc: Alan Cox --- 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 b8b6c4b..174bffa 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -48,6 +48,7 @@ #define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 #define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 #define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 +#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 @@ -55,9 +56,29 @@ #define ADV7180_AUTODETECT_ENABLE_REG 0x07 #define ADV7180_AUTODETECT_DEFAULT 0x7f +#define ADV7180_CON_REG 0x08 /*Unsigned */ +#define CON_REG_MIN 0 +#define CON_REG_DEF 128 +#define CON_REG_MAX 255 + +#define ADV7180_BRI_REG 0x0a /*Signed */ +#define BRI_REG_MIN -128 +#define BRI_REG_DEF 0 +#define BRI_REG_MAX 127 + +#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ +#define HUE_REG_MIN -127 +#define HUE_REG_DEF 0 +#define HUE_REG_MAX 128 + #define ADV7180_ADI_CTRL_REG 0x0e #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 +#define ADV7180_PWR_MAN_REG 0x0f +#define ADV7180_PWR_MAN_ON 0x04 +#define ADV7180_PWR_MAN_OFF 0x24 +#define ADV7180_PWR_MAN_RES 0x80 + #define ADV7180_STATUS1_REG 0x10 #define ADV7180_STATUS1_IN_LOCK 0x01 #define ADV7180_STATUS1_AUTOD_MASK 0x70 @@ -78,6 +99,12 @@ #define ADV7180_ICONF1_PSYNC_ONLY 0x10 #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 +#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ +#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ +#define SAT_REG_MIN 0 +#define SAT_REG_DEF 128 +#define SAT_REG_MAX 255 + #define ADV7180_IRQ1_LOCK 0x01 #define ADV7180_IRQ1_UNLOCK 0x02 #define ADV7180_ISR1_ADI 0x42 @@ -90,6 +117,9 @@ #define ADV7180_IMR3_ADI 0x4C #define ADV7180_IMR4_ADI 0x50 +#define ADV7180_NTSC_V_BIT_END_REG 0xE6 +#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F + struct adv7180_state { struct v4l2_subdev sd; struct work_struct work; @@ -97,6 +127,11 @@ struct adv7180_state { int irq; v4l2_std_id curr_norm; bool autodetect; + s8 brightness; + s16 hue; + u8 contrast; + u8 saturation; + u8 input; }; static v4l2_std_id adv7180_std_to_v4l2(u8 status1) @@ -155,7 +190,7 @@ static u32 adv7180_status_to_v4l2(u8 status1) } static int __adv7180_status(struct i2c_client *client, u32 *status, - v4l2_std_id *std) + v4l2_std_id *std) { int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); @@ -192,6 +227,36 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) return err; } +static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (ret) + return ret; + + /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept + * all inputs and let the card driver take care of validation + */ + if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) + goto out; + + ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); + + if (ret < 0) + goto out; + + ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret | input); + state->input = input; +out: + mutex_unlock(&state->mutex); + return ret; +} + static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct adv7180_state *state = to_state(sd); @@ -205,7 +270,7 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) } static int adv7180_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -222,9 +287,10 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* all standards -> autodetect */ if (std == V4L2_STD_ALL) { - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); if (ret < 0) goto out; @@ -236,7 +302,8 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) goto out; ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, ret); + ADV7180_INPUT_CONTROL_REG, + ret | state->input); if (ret < 0) goto out; @@ -249,14 +316,138 @@ out: return ret; } +static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX, + 1, BRI_REG_DEF); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX, + 1, HUE_REG_DEF); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX, + 1, CON_REG_DEF); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX, + 1, SAT_REG_DEF); + default: + break; + } + + return -EINVAL; +} + +static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->brightness; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if ((ctrl->value > BRI_REG_MAX) + || (ctrl->value < BRI_REG_MIN)) { + ret = -ERANGE; + break; + } + state->brightness = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_BRI_REG, + state->brightness); + break; + case V4L2_CID_HUE: + if ((ctrl->value > HUE_REG_MAX) + || (ctrl->value < HUE_REG_MIN)) { + ret = -ERANGE; + break; + } + state->hue = ctrl->value; + /*Hue is inverted according to HSL chart */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_HUE_REG, -state->hue); + break; + case V4L2_CID_CONTRAST: + if ((ctrl->value > CON_REG_MAX) + || (ctrl->value < CON_REG_MIN)) { + ret = -ERANGE; + break; + } + state->contrast = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_CON_REG, + state->contrast); + break; + case V4L2_CID_SATURATION: + if ((ctrl->value > SAT_REG_MAX) + || (ctrl->value < SAT_REG_MIN)) { + ret = -ERANGE; + break; + } + /* + *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE + *Let's not confuse the user, everybody understands saturation + */ + state->saturation = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CB_REG, + state->saturation); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CR_REG, + state->saturation); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, + .s_routing = adv7180_s_routing, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, .s_std = adv7180_s_std, + .queryctrl = adv7180_queryctrl, + .g_ctrl = adv7180_g_ctrl, + .s_ctrl = adv7180_s_ctrl, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -267,13 +458,13 @@ static const struct v4l2_subdev_ops adv7180_ops = { static void adv7180_work(struct work_struct *work) { struct adv7180_state *state = container_of(work, struct adv7180_state, - work); + work); struct i2c_client *client = v4l2_get_subdevdata(&state->sd); u8 isr3; mutex_lock(&state->mutex); i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); + ADV7180_ADI_CTRL_IRQ_SPACE); isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); /* clear */ i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); @@ -297,56 +488,51 @@ static irqreturn_t adv7180_irq(int irq, void *devid) return IRQ_HANDLED; } -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static __devinit int adv7180_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int init_device(struct i2c_client *client, struct adv7180_state *state) { - struct adv7180_state *state; - struct v4l2_subdev *sd; int ret; - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) { - ret = -ENOMEM; - goto err; - } - - state->irq = client->irq; - INIT_WORK(&state->work, adv7180_work); - mutex_init(&state->mutex); - state->autodetect = true; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - /* Initialize adv7180 */ /* Enable autodetection */ - ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); - if (ret < 0) - goto err_unreg_subdev; + if (state->autodetect) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + return ret; - ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) - goto err_unreg_subdev; + ret = + i2c_smbus_write_byte_data(client, + ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + return ret; + } else { + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + return ret; + + } /* ITU-R BT.656-4 compatible */ ret = i2c_smbus_write_byte_data(client, - ADV7180_EXTENDED_OUTPUT_CONTROL_REG, - ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); if (ret < 0) - goto err_unreg_subdev; + return ret; + + /* Manually set V bit end position in NTSC mode */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_NTSC_V_BIT_END_REG, + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); + if (ret < 0) + return ret; /* read current norm */ __adv7180_status(client, NULL, &state->curr_norm); @@ -354,45 +540,109 @@ static __devinit int adv7180_probe(struct i2c_client *client, /* register for interrupts */ if (state->irq > 0) { ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, - state); + state); if (ret) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); + ADV7180_ADI_CTRL_IRQ_SPACE); if (ret < 0) - goto err_unreg_subdev; + return ret; /* config the Interrupt pin to be active low */ ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, - ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); + ADV7180_ICONF1_ACTIVE_LOW | + ADV7180_ICONF1_PSYNC_ONLY); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); if (ret < 0) - goto err_unreg_subdev; + return ret; /* enable AD change interrupts interrupts */ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, - ADV7180_IRQ3_AD_CHANGE); + ADV7180_IRQ3_AD_CHANGE); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - 0); + 0); if (ret < 0) - goto err_unreg_subdev; + return ret; } + /*Set default value for controls */ + ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, + state->brightness); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, + state->contrast); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, + state->saturation); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, + state->saturation); + if (ret < 0) + return ret; + + return 0; +} + +static __devinit int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7180_state *state; + struct v4l2_subdev *sd; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr, client->adapter->name); + + state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; + state->brightness = BRI_REG_DEF; + state->hue = HUE_REG_DEF; + state->contrast = CON_REG_DEF; + state->saturation = SAT_REG_DEF; + state->input = 0; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + + ret = init_device(client, state); + if (0 != ret) + goto err_unreg_subdev; return 0; err_unreg_subdev: @@ -432,16 +682,49 @@ static const struct i2c_device_id adv7180_id[] = { {}, }; +#ifdef CONFIG_PM +static int adv7180_suspend(struct i2c_client *client, pm_message_t state) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int adv7180_resume(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_ON); + if (ret < 0) + return ret; + ret = init_device(client, state); + if (ret < 0) + return ret; + return 0; +} +#endif + MODULE_DEVICE_TABLE(i2c, adv7180_id); static struct i2c_driver adv7180_driver = { .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - }, - .probe = adv7180_probe, - .remove = __devexit_p(adv7180_remove), - .id_table = adv7180_id, + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = adv7180_probe, + .remove = __devexit_p(adv7180_remove), +#ifdef CONFIG_PM + .suspend = adv7180_suspend, + .resume = adv7180_resume, +#endif + .id_table = adv7180_id, }; module_i2c_driver(adv7180_driver);