1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab * adv7180.c Analog Devices ADV7180 video decoder driver 3cb7a01acSMauro Carvalho Chehab * Copyright (c) 2009 Intel Corporation 4cccb83f7SVladimir Barinov * Copyright (C) 2013 Cogent Embedded, Inc. 5cccb83f7SVladimir Barinov * Copyright (C) 2013 Renesas Solutions Corp. 6cb7a01acSMauro Carvalho Chehab * 7cb7a01acSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 8cb7a01acSMauro Carvalho Chehab * it under the terms of the GNU General Public License version 2 as 9cb7a01acSMauro Carvalho Chehab * published by the Free Software Foundation. 10cb7a01acSMauro Carvalho Chehab * 11cb7a01acSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 12cb7a01acSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 13cb7a01acSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14cb7a01acSMauro Carvalho Chehab * GNU General Public License for more details. 15cb7a01acSMauro Carvalho Chehab * 16cb7a01acSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 17cb7a01acSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 18cb7a01acSMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19cb7a01acSMauro Carvalho Chehab */ 20cb7a01acSMauro Carvalho Chehab 21cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 22cb7a01acSMauro Carvalho Chehab #include <linux/init.h> 23cb7a01acSMauro Carvalho Chehab #include <linux/errno.h> 24cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h> 25cb7a01acSMauro Carvalho Chehab #include <linux/interrupt.h> 26cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 27cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ioctl.h> 29cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 32cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h> 33cb7a01acSMauro Carvalho Chehab 34cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_REG 0x00 35cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 36cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 37cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 38cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 39cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_NTSC_J 0x40 40cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_NTSC_M 0x50 41cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL60 0x60 42cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_NTSC_443 0x70 43cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_BG 0x80 44cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_N 0x90 45cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_M 0xa0 46cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 47cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 48cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 49cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 50cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 51cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f 52cb7a01acSMauro Carvalho Chehab 53cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 54cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 55cb7a01acSMauro Carvalho Chehab 56cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_ENABLE_REG 0x07 57cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_DEFAULT 0x7f 58cb7a01acSMauro Carvalho Chehab /* Contrast */ 59cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_REG 0x08 /*Unsigned */ 60cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MIN 0 61cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_DEF 128 62cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MAX 255 63cb7a01acSMauro Carvalho Chehab /* Brightness*/ 64cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_REG 0x0a /*Signed */ 65cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MIN -128 66cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_DEF 0 67cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MAX 127 68cb7a01acSMauro Carvalho Chehab /* Hue */ 69cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_REG 0x0b /*Signed, inverted */ 70cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MIN -127 71cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_DEF 0 72cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MAX 128 73cb7a01acSMauro Carvalho Chehab 74cb7a01acSMauro Carvalho Chehab #define ADV7180_ADI_CTRL_REG 0x0e 75cb7a01acSMauro Carvalho Chehab #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 76cb7a01acSMauro Carvalho Chehab 77cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_REG 0x0f 78cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_ON 0x04 79cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_OFF 0x24 80cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_RES 0x80 81cb7a01acSMauro Carvalho Chehab 82cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_REG 0x10 83cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_IN_LOCK 0x01 84cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_MASK 0x70 85cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 86cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 87cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_M 0x20 88cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_60 0x30 89cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 90cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM 0x50 91cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 92cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 93cb7a01acSMauro Carvalho Chehab 94cb7a01acSMauro Carvalho Chehab #define ADV7180_IDENT_REG 0x11 95cb7a01acSMauro Carvalho Chehab #define ADV7180_ID_7180 0x18 96cb7a01acSMauro Carvalho Chehab 97cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ADI 0x40 98cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW 0x01 99cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY 0x10 100cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 101cb7a01acSMauro Carvalho Chehab /* Saturation */ 102cb7a01acSMauro Carvalho Chehab #define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ 103cb7a01acSMauro Carvalho Chehab #define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ 104cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN 0 105cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF 128 106cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX 255 107cb7a01acSMauro Carvalho Chehab 108cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK 0x01 109cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK 0x02 110cb7a01acSMauro Carvalho Chehab #define ADV7180_ISR1_ADI 0x42 111cb7a01acSMauro Carvalho Chehab #define ADV7180_ICR1_ADI 0x43 112cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR1_ADI 0x44 113cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR2_ADI 0x48 114cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE 0x08 115cb7a01acSMauro Carvalho Chehab #define ADV7180_ISR3_ADI 0x4A 116cb7a01acSMauro Carvalho Chehab #define ADV7180_ICR3_ADI 0x4B 117cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR3_ADI 0x4C 118cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR4_ADI 0x50 119cb7a01acSMauro Carvalho Chehab 120cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_REG 0xE6 121cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F 122cb7a01acSMauro Carvalho Chehab 123cb7a01acSMauro Carvalho Chehab struct adv7180_state { 124cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler ctrl_hdl; 125cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd; 126cb7a01acSMauro Carvalho Chehab struct mutex mutex; /* mutual excl. when accessing chip */ 127cb7a01acSMauro Carvalho Chehab int irq; 128cb7a01acSMauro Carvalho Chehab v4l2_std_id curr_norm; 129cb7a01acSMauro Carvalho Chehab bool autodetect; 130e246c333SLars-Peter Clausen bool powered; 131cb7a01acSMauro Carvalho Chehab u8 input; 132cb7a01acSMauro Carvalho Chehab }; 133cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ 134cb7a01acSMauro Carvalho Chehab struct adv7180_state, \ 135cb7a01acSMauro Carvalho Chehab ctrl_hdl)->sd) 136cb7a01acSMauro Carvalho Chehab 137cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1) 138cb7a01acSMauro Carvalho Chehab { 139b294a192SVladimir Barinov /* in case V4L2_IN_ST_NO_SIGNAL */ 140b294a192SVladimir Barinov if (!(status1 & ADV7180_STATUS1_IN_LOCK)) 141b294a192SVladimir Barinov return V4L2_STD_UNKNOWN; 142b294a192SVladimir Barinov 143cb7a01acSMauro Carvalho Chehab switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { 144cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_NTSM_M_J: 145cb7a01acSMauro Carvalho Chehab return V4L2_STD_NTSC; 146cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_NTSC_4_43: 147cb7a01acSMauro Carvalho Chehab return V4L2_STD_NTSC_443; 148cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_PAL_M: 149cb7a01acSMauro Carvalho Chehab return V4L2_STD_PAL_M; 150cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_PAL_60: 151cb7a01acSMauro Carvalho Chehab return V4L2_STD_PAL_60; 152cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_PAL_B_G: 153cb7a01acSMauro Carvalho Chehab return V4L2_STD_PAL; 154cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_SECAM: 155cb7a01acSMauro Carvalho Chehab return V4L2_STD_SECAM; 156cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_PAL_COMB: 157cb7a01acSMauro Carvalho Chehab return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; 158cb7a01acSMauro Carvalho Chehab case ADV7180_STATUS1_AUTOD_SECAM_525: 159cb7a01acSMauro Carvalho Chehab return V4L2_STD_SECAM; 160cb7a01acSMauro Carvalho Chehab default: 161cb7a01acSMauro Carvalho Chehab return V4L2_STD_UNKNOWN; 162cb7a01acSMauro Carvalho Chehab } 163cb7a01acSMauro Carvalho Chehab } 164cb7a01acSMauro Carvalho Chehab 165cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std) 166cb7a01acSMauro Carvalho Chehab { 167cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_PAL_60) 168cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_PAL60; 169cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_NTSC_443) 170cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_NTSC_443; 171cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_PAL_N) 172cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_PAL_N; 173cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_PAL_M) 174cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_PAL_M; 175cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_PAL_Nc) 176cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_PAL_COMB_N; 177cb7a01acSMauro Carvalho Chehab 178cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_PAL) 179cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_PAL_BG; 180cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_NTSC) 181cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_NTSC_M; 182cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_SECAM) 183cb7a01acSMauro Carvalho Chehab return ADV7180_INPUT_CONTROL_PAL_SECAM; 184cb7a01acSMauro Carvalho Chehab 185cb7a01acSMauro Carvalho Chehab return -EINVAL; 186cb7a01acSMauro Carvalho Chehab } 187cb7a01acSMauro Carvalho Chehab 188cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1) 189cb7a01acSMauro Carvalho Chehab { 190cb7a01acSMauro Carvalho Chehab if (!(status1 & ADV7180_STATUS1_IN_LOCK)) 191cb7a01acSMauro Carvalho Chehab return V4L2_IN_ST_NO_SIGNAL; 192cb7a01acSMauro Carvalho Chehab 193cb7a01acSMauro Carvalho Chehab return 0; 194cb7a01acSMauro Carvalho Chehab } 195cb7a01acSMauro Carvalho Chehab 196cb7a01acSMauro Carvalho Chehab static int __adv7180_status(struct i2c_client *client, u32 *status, 197cb7a01acSMauro Carvalho Chehab v4l2_std_id *std) 198cb7a01acSMauro Carvalho Chehab { 199cb7a01acSMauro Carvalho Chehab int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); 200cb7a01acSMauro Carvalho Chehab 201cb7a01acSMauro Carvalho Chehab if (status1 < 0) 202cb7a01acSMauro Carvalho Chehab return status1; 203cb7a01acSMauro Carvalho Chehab 204cb7a01acSMauro Carvalho Chehab if (status) 205cb7a01acSMauro Carvalho Chehab *status = adv7180_status_to_v4l2(status1); 206cb7a01acSMauro Carvalho Chehab if (std) 207cb7a01acSMauro Carvalho Chehab *std = adv7180_std_to_v4l2(status1); 208cb7a01acSMauro Carvalho Chehab 209cb7a01acSMauro Carvalho Chehab return 0; 210cb7a01acSMauro Carvalho Chehab } 211cb7a01acSMauro Carvalho Chehab 212cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) 213cb7a01acSMauro Carvalho Chehab { 214cb7a01acSMauro Carvalho Chehab return container_of(sd, struct adv7180_state, sd); 215cb7a01acSMauro Carvalho Chehab } 216cb7a01acSMauro Carvalho Chehab 217cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 218cb7a01acSMauro Carvalho Chehab { 219cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 220cb7a01acSMauro Carvalho Chehab int err = mutex_lock_interruptible(&state->mutex); 221cb7a01acSMauro Carvalho Chehab if (err) 222cb7a01acSMauro Carvalho Chehab return err; 223cb7a01acSMauro Carvalho Chehab 224cb7a01acSMauro Carvalho Chehab /* when we are interrupt driven we know the state */ 225cb7a01acSMauro Carvalho Chehab if (!state->autodetect || state->irq > 0) 226cb7a01acSMauro Carvalho Chehab *std = state->curr_norm; 227cb7a01acSMauro Carvalho Chehab else 228cb7a01acSMauro Carvalho Chehab err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); 229cb7a01acSMauro Carvalho Chehab 230cb7a01acSMauro Carvalho Chehab mutex_unlock(&state->mutex); 231cb7a01acSMauro Carvalho Chehab return err; 232cb7a01acSMauro Carvalho Chehab } 233cb7a01acSMauro Carvalho Chehab 234cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, 235cb7a01acSMauro Carvalho Chehab u32 output, u32 config) 236cb7a01acSMauro Carvalho Chehab { 237cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 238cb7a01acSMauro Carvalho Chehab int ret = mutex_lock_interruptible(&state->mutex); 239cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 240cb7a01acSMauro Carvalho Chehab 241cb7a01acSMauro Carvalho Chehab if (ret) 242cb7a01acSMauro Carvalho Chehab return ret; 243cb7a01acSMauro Carvalho Chehab 244cb7a01acSMauro Carvalho Chehab /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept 245cb7a01acSMauro Carvalho Chehab * all inputs and let the card driver take care of validation 246cb7a01acSMauro Carvalho Chehab */ 247cb7a01acSMauro Carvalho Chehab if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) 248cb7a01acSMauro Carvalho Chehab goto out; 249cb7a01acSMauro Carvalho Chehab 250cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); 251cb7a01acSMauro Carvalho Chehab 252cb7a01acSMauro Carvalho Chehab if (ret < 0) 253cb7a01acSMauro Carvalho Chehab goto out; 254cb7a01acSMauro Carvalho Chehab 255cb7a01acSMauro Carvalho Chehab ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; 256cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, 257cb7a01acSMauro Carvalho Chehab ADV7180_INPUT_CONTROL_REG, ret | input); 258cb7a01acSMauro Carvalho Chehab state->input = input; 259cb7a01acSMauro Carvalho Chehab out: 260cb7a01acSMauro Carvalho Chehab mutex_unlock(&state->mutex); 261cb7a01acSMauro Carvalho Chehab return ret; 262cb7a01acSMauro Carvalho Chehab } 263cb7a01acSMauro Carvalho Chehab 264cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) 265cb7a01acSMauro Carvalho Chehab { 266cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 267cb7a01acSMauro Carvalho Chehab int ret = mutex_lock_interruptible(&state->mutex); 268cb7a01acSMauro Carvalho Chehab if (ret) 269cb7a01acSMauro Carvalho Chehab return ret; 270cb7a01acSMauro Carvalho Chehab 271cb7a01acSMauro Carvalho Chehab ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); 272cb7a01acSMauro Carvalho Chehab mutex_unlock(&state->mutex); 273cb7a01acSMauro Carvalho Chehab return ret; 274cb7a01acSMauro Carvalho Chehab } 275cb7a01acSMauro Carvalho Chehab 276cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) 277cb7a01acSMauro Carvalho Chehab { 278cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 279cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 280cb7a01acSMauro Carvalho Chehab int ret = mutex_lock_interruptible(&state->mutex); 281cb7a01acSMauro Carvalho Chehab if (ret) 282cb7a01acSMauro Carvalho Chehab return ret; 283cb7a01acSMauro Carvalho Chehab 284cb7a01acSMauro Carvalho Chehab /* all standards -> autodetect */ 285cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_ALL) { 286cb7a01acSMauro Carvalho Chehab ret = 287cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, 288cb7a01acSMauro Carvalho Chehab ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 289cb7a01acSMauro Carvalho Chehab | state->input); 290cb7a01acSMauro Carvalho Chehab if (ret < 0) 291cb7a01acSMauro Carvalho Chehab goto out; 292cb7a01acSMauro Carvalho Chehab 293cb7a01acSMauro Carvalho Chehab __adv7180_status(client, NULL, &state->curr_norm); 294cb7a01acSMauro Carvalho Chehab state->autodetect = true; 295cb7a01acSMauro Carvalho Chehab } else { 296cb7a01acSMauro Carvalho Chehab ret = v4l2_std_to_adv7180(std); 297cb7a01acSMauro Carvalho Chehab if (ret < 0) 298cb7a01acSMauro Carvalho Chehab goto out; 299cb7a01acSMauro Carvalho Chehab 300cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, 301cb7a01acSMauro Carvalho Chehab ADV7180_INPUT_CONTROL_REG, 302cb7a01acSMauro Carvalho Chehab ret | state->input); 303cb7a01acSMauro Carvalho Chehab if (ret < 0) 304cb7a01acSMauro Carvalho Chehab goto out; 305cb7a01acSMauro Carvalho Chehab 306cb7a01acSMauro Carvalho Chehab state->curr_norm = std; 307cb7a01acSMauro Carvalho Chehab state->autodetect = false; 308cb7a01acSMauro Carvalho Chehab } 309cb7a01acSMauro Carvalho Chehab ret = 0; 310cb7a01acSMauro Carvalho Chehab out: 311cb7a01acSMauro Carvalho Chehab mutex_unlock(&state->mutex); 312cb7a01acSMauro Carvalho Chehab return ret; 313cb7a01acSMauro Carvalho Chehab } 314cb7a01acSMauro Carvalho Chehab 315e246c333SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state, 316e246c333SLars-Peter Clausen struct i2c_client *client, bool on) 317e246c333SLars-Peter Clausen { 318e246c333SLars-Peter Clausen u8 val; 319e246c333SLars-Peter Clausen 320e246c333SLars-Peter Clausen if (on) 321e246c333SLars-Peter Clausen val = ADV7180_PWR_MAN_ON; 322e246c333SLars-Peter Clausen else 323e246c333SLars-Peter Clausen val = ADV7180_PWR_MAN_OFF; 324e246c333SLars-Peter Clausen 325e246c333SLars-Peter Clausen return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val); 326e246c333SLars-Peter Clausen } 327e246c333SLars-Peter Clausen 328e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on) 329e246c333SLars-Peter Clausen { 330e246c333SLars-Peter Clausen struct adv7180_state *state = to_state(sd); 331e246c333SLars-Peter Clausen struct i2c_client *client = v4l2_get_subdevdata(sd); 332e246c333SLars-Peter Clausen int ret; 333e246c333SLars-Peter Clausen 334e246c333SLars-Peter Clausen ret = mutex_lock_interruptible(&state->mutex); 335e246c333SLars-Peter Clausen if (ret) 336e246c333SLars-Peter Clausen return ret; 337e246c333SLars-Peter Clausen 338e246c333SLars-Peter Clausen ret = adv7180_set_power(state, client, on); 339e246c333SLars-Peter Clausen if (ret == 0) 340e246c333SLars-Peter Clausen state->powered = on; 341e246c333SLars-Peter Clausen 342e246c333SLars-Peter Clausen mutex_unlock(&state->mutex); 343e246c333SLars-Peter Clausen return ret; 344e246c333SLars-Peter Clausen } 345e246c333SLars-Peter Clausen 346cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) 347cb7a01acSMauro Carvalho Chehab { 348cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = to_adv7180_sd(ctrl); 349cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 350cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 351cb7a01acSMauro Carvalho Chehab int ret = mutex_lock_interruptible(&state->mutex); 352cb7a01acSMauro Carvalho Chehab int val; 353cb7a01acSMauro Carvalho Chehab 354cb7a01acSMauro Carvalho Chehab if (ret) 355cb7a01acSMauro Carvalho Chehab return ret; 356cb7a01acSMauro Carvalho Chehab val = ctrl->val; 357cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 358cb7a01acSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 359cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); 360cb7a01acSMauro Carvalho Chehab break; 361cb7a01acSMauro Carvalho Chehab case V4L2_CID_HUE: 362cb7a01acSMauro Carvalho Chehab /*Hue is inverted according to HSL chart */ 363cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); 364cb7a01acSMauro Carvalho Chehab break; 365cb7a01acSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 366cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); 367cb7a01acSMauro Carvalho Chehab break; 368cb7a01acSMauro Carvalho Chehab case V4L2_CID_SATURATION: 369cb7a01acSMauro Carvalho Chehab /* 370cb7a01acSMauro Carvalho Chehab *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE 371cb7a01acSMauro Carvalho Chehab *Let's not confuse the user, everybody understands saturation 372cb7a01acSMauro Carvalho Chehab */ 373cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, 374cb7a01acSMauro Carvalho Chehab val); 375cb7a01acSMauro Carvalho Chehab if (ret < 0) 376cb7a01acSMauro Carvalho Chehab break; 377cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, 378cb7a01acSMauro Carvalho Chehab val); 379cb7a01acSMauro Carvalho Chehab break; 380cb7a01acSMauro Carvalho Chehab default: 381cb7a01acSMauro Carvalho Chehab ret = -EINVAL; 382cb7a01acSMauro Carvalho Chehab } 383cb7a01acSMauro Carvalho Chehab 384cb7a01acSMauro Carvalho Chehab mutex_unlock(&state->mutex); 385cb7a01acSMauro Carvalho Chehab return ret; 386cb7a01acSMauro Carvalho Chehab } 387cb7a01acSMauro Carvalho Chehab 388cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { 389cb7a01acSMauro Carvalho Chehab .s_ctrl = adv7180_s_ctrl, 390cb7a01acSMauro Carvalho Chehab }; 391cb7a01acSMauro Carvalho Chehab 392cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state) 393cb7a01acSMauro Carvalho Chehab { 394cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); 395cb7a01acSMauro Carvalho Chehab 396cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 397cb7a01acSMauro Carvalho Chehab V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, 398cb7a01acSMauro Carvalho Chehab ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); 399cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 400cb7a01acSMauro Carvalho Chehab V4L2_CID_CONTRAST, ADV7180_CON_MIN, 401cb7a01acSMauro Carvalho Chehab ADV7180_CON_MAX, 1, ADV7180_CON_DEF); 402cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 403cb7a01acSMauro Carvalho Chehab V4L2_CID_SATURATION, ADV7180_SAT_MIN, 404cb7a01acSMauro Carvalho Chehab ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); 405cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 406cb7a01acSMauro Carvalho Chehab V4L2_CID_HUE, ADV7180_HUE_MIN, 407cb7a01acSMauro Carvalho Chehab ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); 408cb7a01acSMauro Carvalho Chehab state->sd.ctrl_handler = &state->ctrl_hdl; 409cb7a01acSMauro Carvalho Chehab if (state->ctrl_hdl.error) { 410cb7a01acSMauro Carvalho Chehab int err = state->ctrl_hdl.error; 411cb7a01acSMauro Carvalho Chehab 412cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&state->ctrl_hdl); 413cb7a01acSMauro Carvalho Chehab return err; 414cb7a01acSMauro Carvalho Chehab } 415cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_setup(&state->ctrl_hdl); 416cb7a01acSMauro Carvalho Chehab 417cb7a01acSMauro Carvalho Chehab return 0; 418cb7a01acSMauro Carvalho Chehab } 419cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state) 420cb7a01acSMauro Carvalho Chehab { 421cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&state->ctrl_hdl); 422cb7a01acSMauro Carvalho Chehab } 423cb7a01acSMauro Carvalho Chehab 424cccb83f7SVladimir Barinov static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, 425f5fe58fdSBoris BREZILLON u32 *code) 426cccb83f7SVladimir Barinov { 427cccb83f7SVladimir Barinov if (index > 0) 428cccb83f7SVladimir Barinov return -EINVAL; 429cccb83f7SVladimir Barinov 430f5fe58fdSBoris BREZILLON *code = MEDIA_BUS_FMT_YUYV8_2X8; 431cccb83f7SVladimir Barinov 432cccb83f7SVladimir Barinov return 0; 433cccb83f7SVladimir Barinov } 434cccb83f7SVladimir Barinov 435cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd, 436cccb83f7SVladimir Barinov struct v4l2_mbus_framefmt *fmt) 437cccb83f7SVladimir Barinov { 438cccb83f7SVladimir Barinov struct adv7180_state *state = to_state(sd); 439cccb83f7SVladimir Barinov 440f5fe58fdSBoris BREZILLON fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; 441cccb83f7SVladimir Barinov fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; 442cccb83f7SVladimir Barinov fmt->field = V4L2_FIELD_INTERLACED; 443cccb83f7SVladimir Barinov fmt->width = 720; 444cccb83f7SVladimir Barinov fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; 445cccb83f7SVladimir Barinov 446cccb83f7SVladimir Barinov return 0; 447cccb83f7SVladimir Barinov } 448cccb83f7SVladimir Barinov 449cccb83f7SVladimir Barinov static int adv7180_g_mbus_config(struct v4l2_subdev *sd, 450cccb83f7SVladimir Barinov struct v4l2_mbus_config *cfg) 451cccb83f7SVladimir Barinov { 452cccb83f7SVladimir Barinov /* 453cccb83f7SVladimir Barinov * The ADV7180 sensor supports BT.601/656 output modes. 454cccb83f7SVladimir Barinov * The BT.656 is default and not yet configurable by s/w. 455cccb83f7SVladimir Barinov */ 456cccb83f7SVladimir Barinov cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | 457cccb83f7SVladimir Barinov V4L2_MBUS_DATA_ACTIVE_HIGH; 458cccb83f7SVladimir Barinov cfg->type = V4L2_MBUS_BT656; 459cccb83f7SVladimir Barinov 460cccb83f7SVladimir Barinov return 0; 461cccb83f7SVladimir Barinov } 462cccb83f7SVladimir Barinov 463cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = { 4648774bed9SLaurent Pinchart .s_std = adv7180_s_std, 465cb7a01acSMauro Carvalho Chehab .querystd = adv7180_querystd, 466cb7a01acSMauro Carvalho Chehab .g_input_status = adv7180_g_input_status, 467cb7a01acSMauro Carvalho Chehab .s_routing = adv7180_s_routing, 468cccb83f7SVladimir Barinov .enum_mbus_fmt = adv7180_enum_mbus_fmt, 469cccb83f7SVladimir Barinov .try_mbus_fmt = adv7180_mbus_fmt, 470cccb83f7SVladimir Barinov .g_mbus_fmt = adv7180_mbus_fmt, 471cccb83f7SVladimir Barinov .s_mbus_fmt = adv7180_mbus_fmt, 472cccb83f7SVladimir Barinov .g_mbus_config = adv7180_g_mbus_config, 473cb7a01acSMauro Carvalho Chehab }; 474cb7a01acSMauro Carvalho Chehab 475cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = { 476e246c333SLars-Peter Clausen .s_power = adv7180_s_power, 477cb7a01acSMauro Carvalho Chehab }; 478cb7a01acSMauro Carvalho Chehab 479cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = { 480cb7a01acSMauro Carvalho Chehab .core = &adv7180_core_ops, 481cb7a01acSMauro Carvalho Chehab .video = &adv7180_video_ops, 482cb7a01acSMauro Carvalho Chehab }; 483cb7a01acSMauro Carvalho Chehab 4840c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid) 485cb7a01acSMauro Carvalho Chehab { 4860c25534dSLars-Peter Clausen struct adv7180_state *state = devid; 487cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&state->sd); 488cb7a01acSMauro Carvalho Chehab u8 isr3; 489cb7a01acSMauro Carvalho Chehab 490cb7a01acSMauro Carvalho Chehab mutex_lock(&state->mutex); 491cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 492cb7a01acSMauro Carvalho Chehab ADV7180_ADI_CTRL_IRQ_SPACE); 493cb7a01acSMauro Carvalho Chehab isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); 494cb7a01acSMauro Carvalho Chehab /* clear */ 495cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); 496cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); 497cb7a01acSMauro Carvalho Chehab 498cb7a01acSMauro Carvalho Chehab if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) 499cb7a01acSMauro Carvalho Chehab __adv7180_status(client, NULL, &state->curr_norm); 500cb7a01acSMauro Carvalho Chehab mutex_unlock(&state->mutex); 501cb7a01acSMauro Carvalho Chehab 502cb7a01acSMauro Carvalho Chehab return IRQ_HANDLED; 503cb7a01acSMauro Carvalho Chehab } 504cb7a01acSMauro Carvalho Chehab 505cb7a01acSMauro Carvalho Chehab static int init_device(struct i2c_client *client, struct adv7180_state *state) 506cb7a01acSMauro Carvalho Chehab { 507cb7a01acSMauro Carvalho Chehab int ret; 508cb7a01acSMauro Carvalho Chehab 509cb7a01acSMauro Carvalho Chehab /* Initialize adv7180 */ 510cb7a01acSMauro Carvalho Chehab /* Enable autodetection */ 511cb7a01acSMauro Carvalho Chehab if (state->autodetect) { 512cb7a01acSMauro Carvalho Chehab ret = 513cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, 514cb7a01acSMauro Carvalho Chehab ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 515cb7a01acSMauro Carvalho Chehab | state->input); 516cb7a01acSMauro Carvalho Chehab if (ret < 0) 517cb7a01acSMauro Carvalho Chehab return ret; 518cb7a01acSMauro Carvalho Chehab 519cb7a01acSMauro Carvalho Chehab ret = 520cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, 521cb7a01acSMauro Carvalho Chehab ADV7180_AUTODETECT_ENABLE_REG, 522cb7a01acSMauro Carvalho Chehab ADV7180_AUTODETECT_DEFAULT); 523cb7a01acSMauro Carvalho Chehab if (ret < 0) 524cb7a01acSMauro Carvalho Chehab return ret; 525cb7a01acSMauro Carvalho Chehab } else { 526cb7a01acSMauro Carvalho Chehab ret = v4l2_std_to_adv7180(state->curr_norm); 527cb7a01acSMauro Carvalho Chehab if (ret < 0) 528cb7a01acSMauro Carvalho Chehab return ret; 529cb7a01acSMauro Carvalho Chehab 530cb7a01acSMauro Carvalho Chehab ret = 531cb7a01acSMauro Carvalho Chehab i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, 532cb7a01acSMauro Carvalho Chehab ret | state->input); 533cb7a01acSMauro Carvalho Chehab if (ret < 0) 534cb7a01acSMauro Carvalho Chehab return ret; 535cb7a01acSMauro Carvalho Chehab 536cb7a01acSMauro Carvalho Chehab } 537cb7a01acSMauro Carvalho Chehab /* ITU-R BT.656-4 compatible */ 538cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, 539cb7a01acSMauro Carvalho Chehab ADV7180_EXTENDED_OUTPUT_CONTROL_REG, 540cb7a01acSMauro Carvalho Chehab ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); 541cb7a01acSMauro Carvalho Chehab if (ret < 0) 542cb7a01acSMauro Carvalho Chehab return ret; 543cb7a01acSMauro Carvalho Chehab 544cb7a01acSMauro Carvalho Chehab /* Manually set V bit end position in NTSC mode */ 545cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, 546cb7a01acSMauro Carvalho Chehab ADV7180_NTSC_V_BIT_END_REG, 547cb7a01acSMauro Carvalho Chehab ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); 548cb7a01acSMauro Carvalho Chehab if (ret < 0) 549cb7a01acSMauro Carvalho Chehab return ret; 550cb7a01acSMauro Carvalho Chehab 551cb7a01acSMauro Carvalho Chehab /* read current norm */ 552cb7a01acSMauro Carvalho Chehab __adv7180_status(client, NULL, &state->curr_norm); 553cb7a01acSMauro Carvalho Chehab 554cb7a01acSMauro Carvalho Chehab /* register for interrupts */ 555cb7a01acSMauro Carvalho Chehab if (state->irq > 0) { 556cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 557cb7a01acSMauro Carvalho Chehab ADV7180_ADI_CTRL_IRQ_SPACE); 558cb7a01acSMauro Carvalho Chehab if (ret < 0) 559df065b37SAlexey Khoroshilov goto err; 560cb7a01acSMauro Carvalho Chehab 561cb7a01acSMauro Carvalho Chehab /* config the Interrupt pin to be active low */ 562cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, 563cb7a01acSMauro Carvalho Chehab ADV7180_ICONF1_ACTIVE_LOW | 564cb7a01acSMauro Carvalho Chehab ADV7180_ICONF1_PSYNC_ONLY); 565cb7a01acSMauro Carvalho Chehab if (ret < 0) 566df065b37SAlexey Khoroshilov goto err; 567cb7a01acSMauro Carvalho Chehab 568cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); 569cb7a01acSMauro Carvalho Chehab if (ret < 0) 570df065b37SAlexey Khoroshilov goto err; 571cb7a01acSMauro Carvalho Chehab 572cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); 573cb7a01acSMauro Carvalho Chehab if (ret < 0) 574df065b37SAlexey Khoroshilov goto err; 575cb7a01acSMauro Carvalho Chehab 576cb7a01acSMauro Carvalho Chehab /* enable AD change interrupts interrupts */ 577cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, 578cb7a01acSMauro Carvalho Chehab ADV7180_IRQ3_AD_CHANGE); 579cb7a01acSMauro Carvalho Chehab if (ret < 0) 580df065b37SAlexey Khoroshilov goto err; 581cb7a01acSMauro Carvalho Chehab 582cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); 583cb7a01acSMauro Carvalho Chehab if (ret < 0) 584df065b37SAlexey Khoroshilov goto err; 585cb7a01acSMauro Carvalho Chehab 586cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 587cb7a01acSMauro Carvalho Chehab 0); 588cb7a01acSMauro Carvalho Chehab if (ret < 0) 589df065b37SAlexey Khoroshilov goto err; 590cb7a01acSMauro Carvalho Chehab } 591cb7a01acSMauro Carvalho Chehab 592cb7a01acSMauro Carvalho Chehab return 0; 593df065b37SAlexey Khoroshilov 594df065b37SAlexey Khoroshilov err: 595df065b37SAlexey Khoroshilov return ret; 596cb7a01acSMauro Carvalho Chehab } 597cb7a01acSMauro Carvalho Chehab 5984c62e976SGreg Kroah-Hartman static int adv7180_probe(struct i2c_client *client, 599cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *id) 600cb7a01acSMauro Carvalho Chehab { 601cb7a01acSMauro Carvalho Chehab struct adv7180_state *state; 602cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd; 603cb7a01acSMauro Carvalho Chehab int ret; 604cb7a01acSMauro Carvalho Chehab 605cb7a01acSMauro Carvalho Chehab /* Check if the adapter supports the needed features */ 606cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 607cb7a01acSMauro Carvalho Chehab return -EIO; 608cb7a01acSMauro Carvalho Chehab 609cb7a01acSMauro Carvalho Chehab v4l_info(client, "chip found @ 0x%02x (%s)\n", 610cb7a01acSMauro Carvalho Chehab client->addr, client->adapter->name); 611cb7a01acSMauro Carvalho Chehab 612c02b211dSLaurent Pinchart state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); 6137657e064SFabio Estevam if (state == NULL) 6147657e064SFabio Estevam return -ENOMEM; 615cb7a01acSMauro Carvalho Chehab 616cb7a01acSMauro Carvalho Chehab state->irq = client->irq; 617cb7a01acSMauro Carvalho Chehab mutex_init(&state->mutex); 618cb7a01acSMauro Carvalho Chehab state->autodetect = true; 619e246c333SLars-Peter Clausen state->powered = true; 620cb7a01acSMauro Carvalho Chehab state->input = 0; 621cb7a01acSMauro Carvalho Chehab sd = &state->sd; 622cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &adv7180_ops); 623cb7a01acSMauro Carvalho Chehab 624cb7a01acSMauro Carvalho Chehab ret = adv7180_init_controls(state); 625cb7a01acSMauro Carvalho Chehab if (ret) 626cb7a01acSMauro Carvalho Chehab goto err_unreg_subdev; 627cb7a01acSMauro Carvalho Chehab ret = init_device(client, state); 628cb7a01acSMauro Carvalho Chehab if (ret) 629cb7a01acSMauro Carvalho Chehab goto err_free_ctrl; 630fa5b7945SLars-Peter Clausen 631fa5721d1SLars-Peter Clausen if (state->irq) { 632fa5721d1SLars-Peter Clausen ret = request_threaded_irq(client->irq, NULL, adv7180_irq, 633fa5721d1SLars-Peter Clausen IRQF_ONESHOT, KBUILD_MODNAME, state); 634fa5721d1SLars-Peter Clausen if (ret) 635fa5721d1SLars-Peter Clausen goto err_free_ctrl; 636fa5721d1SLars-Peter Clausen } 637fa5721d1SLars-Peter Clausen 638fa5b7945SLars-Peter Clausen ret = v4l2_async_register_subdev(sd); 639fa5b7945SLars-Peter Clausen if (ret) 640fa5b7945SLars-Peter Clausen goto err_free_irq; 641fa5b7945SLars-Peter Clausen 642cb7a01acSMauro Carvalho Chehab return 0; 643cb7a01acSMauro Carvalho Chehab 644fa5b7945SLars-Peter Clausen err_free_irq: 645fa5b7945SLars-Peter Clausen if (state->irq > 0) 646fa5b7945SLars-Peter Clausen free_irq(client->irq, state); 647cb7a01acSMauro Carvalho Chehab err_free_ctrl: 648cb7a01acSMauro Carvalho Chehab adv7180_exit_controls(state); 649cb7a01acSMauro Carvalho Chehab err_unreg_subdev: 650297a0ae3SLars-Peter Clausen mutex_destroy(&state->mutex); 651cb7a01acSMauro Carvalho Chehab return ret; 652cb7a01acSMauro Carvalho Chehab } 653cb7a01acSMauro Carvalho Chehab 6544c62e976SGreg Kroah-Hartman static int adv7180_remove(struct i2c_client *client) 655cb7a01acSMauro Carvalho Chehab { 656cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 657cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 658cb7a01acSMauro Carvalho Chehab 659fa5b7945SLars-Peter Clausen v4l2_async_unregister_subdev(sd); 660fa5b7945SLars-Peter Clausen 6610c25534dSLars-Peter Clausen if (state->irq > 0) 662cb7a01acSMauro Carvalho Chehab free_irq(client->irq, state); 663cb7a01acSMauro Carvalho Chehab 664b13f4af2SLars-Peter Clausen adv7180_exit_controls(state); 665297a0ae3SLars-Peter Clausen mutex_destroy(&state->mutex); 666cb7a01acSMauro Carvalho Chehab return 0; 667cb7a01acSMauro Carvalho Chehab } 668cb7a01acSMauro Carvalho Chehab 669cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = { 670cb7a01acSMauro Carvalho Chehab {KBUILD_MODNAME, 0}, 671cb7a01acSMauro Carvalho Chehab {}, 672cb7a01acSMauro Carvalho Chehab }; 673cb7a01acSMauro Carvalho Chehab 674cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP 675cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev) 676cb7a01acSMauro Carvalho Chehab { 677cc1088dcSLars-Peter Clausen struct i2c_client *client = to_i2c_client(dev); 678e246c333SLars-Peter Clausen struct v4l2_subdev *sd = i2c_get_clientdata(client); 679e246c333SLars-Peter Clausen struct adv7180_state *state = to_state(sd); 680cb7a01acSMauro Carvalho Chehab 681e246c333SLars-Peter Clausen return adv7180_set_power(state, client, false); 682cb7a01acSMauro Carvalho Chehab } 683cb7a01acSMauro Carvalho Chehab 684cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev) 685cb7a01acSMauro Carvalho Chehab { 686cc1088dcSLars-Peter Clausen struct i2c_client *client = to_i2c_client(dev); 687cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 688cb7a01acSMauro Carvalho Chehab struct adv7180_state *state = to_state(sd); 689cb7a01acSMauro Carvalho Chehab int ret; 690cb7a01acSMauro Carvalho Chehab 691e246c333SLars-Peter Clausen if (state->powered) { 692e246c333SLars-Peter Clausen ret = adv7180_set_power(state, client, true); 693e246c333SLars-Peter Clausen if (ret) 694cb7a01acSMauro Carvalho Chehab return ret; 695e246c333SLars-Peter Clausen } 696cb7a01acSMauro Carvalho Chehab ret = init_device(client, state); 697cb7a01acSMauro Carvalho Chehab if (ret < 0) 698cb7a01acSMauro Carvalho Chehab return ret; 699cb7a01acSMauro Carvalho Chehab return 0; 700cb7a01acSMauro Carvalho Chehab } 701cc1088dcSLars-Peter Clausen 702cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); 703cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops) 704cc1088dcSLars-Peter Clausen 705cc1088dcSLars-Peter Clausen #else 706cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL 707cb7a01acSMauro Carvalho Chehab #endif 708cb7a01acSMauro Carvalho Chehab 709cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adv7180_id); 710cb7a01acSMauro Carvalho Chehab 711cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = { 712cb7a01acSMauro Carvalho Chehab .driver = { 713cb7a01acSMauro Carvalho Chehab .owner = THIS_MODULE, 714cb7a01acSMauro Carvalho Chehab .name = KBUILD_MODNAME, 715cc1088dcSLars-Peter Clausen .pm = ADV7180_PM_OPS, 716cb7a01acSMauro Carvalho Chehab }, 717cb7a01acSMauro Carvalho Chehab .probe = adv7180_probe, 7184c62e976SGreg Kroah-Hartman .remove = adv7180_remove, 719cb7a01acSMauro Carvalho Chehab .id_table = adv7180_id, 720cb7a01acSMauro Carvalho Chehab }; 721cb7a01acSMauro Carvalho Chehab 722cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver); 723cb7a01acSMauro Carvalho Chehab 724cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); 725cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories"); 726cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2"); 727