1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab * drivers/media/i2c/adp1653.c 3cb7a01acSMauro Carvalho Chehab * 4cb7a01acSMauro Carvalho Chehab * Copyright (C) 2008--2011 Nokia Corporation 5cb7a01acSMauro Carvalho Chehab * 68c5dff90SSakari Ailus * Contact: Sakari Ailus <sakari.ailus@iki.fi> 7cb7a01acSMauro Carvalho Chehab * 8cb7a01acSMauro Carvalho Chehab * Contributors: 98c5dff90SSakari Ailus * Sakari Ailus <sakari.ailus@iki.fi> 10cb7a01acSMauro Carvalho Chehab * Tuukka Toivonen <tuukkat76@gmail.com> 11cb7a01acSMauro Carvalho Chehab * 12cb7a01acSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or 13cb7a01acSMauro Carvalho Chehab * modify it under the terms of the GNU General Public License 14cb7a01acSMauro Carvalho Chehab * version 2 as published by the Free Software Foundation. 15cb7a01acSMauro Carvalho Chehab * 16cb7a01acSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, but 17cb7a01acSMauro Carvalho Chehab * WITHOUT ANY WARRANTY; without even the implied warranty of 18cb7a01acSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19cb7a01acSMauro Carvalho Chehab * General Public License for more details. 20cb7a01acSMauro Carvalho Chehab * 21cb7a01acSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 22cb7a01acSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 23cb7a01acSMauro Carvalho Chehab * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 24cb7a01acSMauro Carvalho Chehab * 02110-1301 USA 25cb7a01acSMauro Carvalho Chehab * 26cb7a01acSMauro Carvalho Chehab * TODO: 27cb7a01acSMauro Carvalho Chehab * - fault interrupt handling 28cb7a01acSMauro Carvalho Chehab * - hardware strobe 29cb7a01acSMauro Carvalho Chehab * - power doesn't need to be ON if all lights are off 30cb7a01acSMauro Carvalho Chehab * 31cb7a01acSMauro Carvalho Chehab */ 32cb7a01acSMauro Carvalho Chehab 33cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 34cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 35cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 36cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 37cb7a01acSMauro Carvalho Chehab #include <media/adp1653.h> 38cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 39cb7a01acSMauro Carvalho Chehab 40cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MAX 820000 41cb7a01acSMauro Carvalho Chehab #define TIMEOUT_STEP 54600 42cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ 43cb7a01acSMauro Carvalho Chehab * TIMEOUT_STEP) 44cb7a01acSMauro Carvalho Chehab #define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ 45cb7a01acSMauro Carvalho Chehab / TIMEOUT_STEP) 46cb7a01acSMauro Carvalho Chehab #define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) 47cb7a01acSMauro Carvalho Chehab 48cb7a01acSMauro Carvalho Chehab /* Write values into ADP1653 registers. */ 49cb7a01acSMauro Carvalho Chehab static int adp1653_update_hw(struct adp1653_flash *flash) 50cb7a01acSMauro Carvalho Chehab { 51cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 52cb7a01acSMauro Carvalho Chehab u8 out_sel; 53cb7a01acSMauro Carvalho Chehab u8 config = 0; 54cb7a01acSMauro Carvalho Chehab int rval; 55cb7a01acSMauro Carvalho Chehab 56cb7a01acSMauro Carvalho Chehab out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( 57cb7a01acSMauro Carvalho Chehab flash->indicator_intensity->val) 58cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_ILED_SHIFT; 59cb7a01acSMauro Carvalho Chehab 60cb7a01acSMauro Carvalho Chehab switch (flash->led_mode->val) { 61cb7a01acSMauro Carvalho Chehab case V4L2_FLASH_LED_MODE_NONE: 62cb7a01acSMauro Carvalho Chehab break; 63cb7a01acSMauro Carvalho Chehab case V4L2_FLASH_LED_MODE_FLASH: 64cb7a01acSMauro Carvalho Chehab /* Flash mode, light on with strobe, duration from timer */ 65cb7a01acSMauro Carvalho Chehab config = ADP1653_REG_CONFIG_TMR_CFG; 66cb7a01acSMauro Carvalho Chehab config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) 67cb7a01acSMauro Carvalho Chehab << ADP1653_REG_CONFIG_TMR_SET_SHIFT; 68cb7a01acSMauro Carvalho Chehab break; 69cb7a01acSMauro Carvalho Chehab case V4L2_FLASH_LED_MODE_TORCH: 70cb7a01acSMauro Carvalho Chehab /* Torch mode, light immediately on, duration indefinite */ 71cb7a01acSMauro Carvalho Chehab out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( 72cb7a01acSMauro Carvalho Chehab flash->torch_intensity->val) 73cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_HPLED_SHIFT; 74cb7a01acSMauro Carvalho Chehab break; 75cb7a01acSMauro Carvalho Chehab } 76cb7a01acSMauro Carvalho Chehab 77cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); 78cb7a01acSMauro Carvalho Chehab if (rval < 0) 79cb7a01acSMauro Carvalho Chehab return rval; 80cb7a01acSMauro Carvalho Chehab 81cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); 82cb7a01acSMauro Carvalho Chehab if (rval < 0) 83cb7a01acSMauro Carvalho Chehab return rval; 84cb7a01acSMauro Carvalho Chehab 85cb7a01acSMauro Carvalho Chehab return 0; 86cb7a01acSMauro Carvalho Chehab } 87cb7a01acSMauro Carvalho Chehab 88cb7a01acSMauro Carvalho Chehab static int adp1653_get_fault(struct adp1653_flash *flash) 89cb7a01acSMauro Carvalho Chehab { 90cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 91cb7a01acSMauro Carvalho Chehab int fault; 92cb7a01acSMauro Carvalho Chehab int rval; 93cb7a01acSMauro Carvalho Chehab 94cb7a01acSMauro Carvalho Chehab fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); 95cb7a01acSMauro Carvalho Chehab if (IS_ERR_VALUE(fault)) 96cb7a01acSMauro Carvalho Chehab return fault; 97cb7a01acSMauro Carvalho Chehab 98cb7a01acSMauro Carvalho Chehab flash->fault |= fault; 99cb7a01acSMauro Carvalho Chehab 100cb7a01acSMauro Carvalho Chehab if (!flash->fault) 101cb7a01acSMauro Carvalho Chehab return 0; 102cb7a01acSMauro Carvalho Chehab 103cb7a01acSMauro Carvalho Chehab /* Clear faults. */ 104cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); 105cb7a01acSMauro Carvalho Chehab if (IS_ERR_VALUE(rval)) 106cb7a01acSMauro Carvalho Chehab return rval; 107cb7a01acSMauro Carvalho Chehab 108cb7a01acSMauro Carvalho Chehab flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; 109cb7a01acSMauro Carvalho Chehab 110cb7a01acSMauro Carvalho Chehab rval = adp1653_update_hw(flash); 111cb7a01acSMauro Carvalho Chehab if (IS_ERR_VALUE(rval)) 112cb7a01acSMauro Carvalho Chehab return rval; 113cb7a01acSMauro Carvalho Chehab 114cb7a01acSMauro Carvalho Chehab return flash->fault; 115cb7a01acSMauro Carvalho Chehab } 116cb7a01acSMauro Carvalho Chehab 117cb7a01acSMauro Carvalho Chehab static int adp1653_strobe(struct adp1653_flash *flash, int enable) 118cb7a01acSMauro Carvalho Chehab { 119cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 120cb7a01acSMauro Carvalho Chehab u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( 121cb7a01acSMauro Carvalho Chehab flash->indicator_intensity->val) 122cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_ILED_SHIFT; 123cb7a01acSMauro Carvalho Chehab int rval; 124cb7a01acSMauro Carvalho Chehab 125cb7a01acSMauro Carvalho Chehab if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) 126cb7a01acSMauro Carvalho Chehab return -EBUSY; 127cb7a01acSMauro Carvalho Chehab 128cb7a01acSMauro Carvalho Chehab if (!enable) 129cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 130cb7a01acSMauro Carvalho Chehab out_sel); 131cb7a01acSMauro Carvalho Chehab 132cb7a01acSMauro Carvalho Chehab out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( 133cb7a01acSMauro Carvalho Chehab flash->flash_intensity->val) 134cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_HPLED_SHIFT; 135cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); 136cb7a01acSMauro Carvalho Chehab if (rval) 137cb7a01acSMauro Carvalho Chehab return rval; 138cb7a01acSMauro Carvalho Chehab 139cb7a01acSMauro Carvalho Chehab /* Software strobe using i2c */ 140cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 141cb7a01acSMauro Carvalho Chehab ADP1653_REG_SW_STROBE_SW_STROBE); 142cb7a01acSMauro Carvalho Chehab if (rval) 143cb7a01acSMauro Carvalho Chehab return rval; 144cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); 145cb7a01acSMauro Carvalho Chehab } 146cb7a01acSMauro Carvalho Chehab 147cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 148cb7a01acSMauro Carvalho Chehab * V4L2 controls 149cb7a01acSMauro Carvalho Chehab */ 150cb7a01acSMauro Carvalho Chehab 151cb7a01acSMauro Carvalho Chehab static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) 152cb7a01acSMauro Carvalho Chehab { 153cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = 154cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct adp1653_flash, ctrls); 155cb7a01acSMauro Carvalho Chehab int rval; 156cb7a01acSMauro Carvalho Chehab 157cb7a01acSMauro Carvalho Chehab rval = adp1653_get_fault(flash); 158cb7a01acSMauro Carvalho Chehab if (IS_ERR_VALUE(rval)) 159cb7a01acSMauro Carvalho Chehab return rval; 160cb7a01acSMauro Carvalho Chehab 161cb7a01acSMauro Carvalho Chehab ctrl->cur.val = 0; 162cb7a01acSMauro Carvalho Chehab 163cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) 164cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; 165cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_OT) 166cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; 167cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) 168cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; 169cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_OV) 170cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; 171cb7a01acSMauro Carvalho Chehab 172cb7a01acSMauro Carvalho Chehab flash->fault = 0; 173cb7a01acSMauro Carvalho Chehab 174cb7a01acSMauro Carvalho Chehab return 0; 175cb7a01acSMauro Carvalho Chehab } 176cb7a01acSMauro Carvalho Chehab 177cb7a01acSMauro Carvalho Chehab static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) 178cb7a01acSMauro Carvalho Chehab { 179cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = 180cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct adp1653_flash, ctrls); 181cb7a01acSMauro Carvalho Chehab int rval; 182cb7a01acSMauro Carvalho Chehab 183cb7a01acSMauro Carvalho Chehab rval = adp1653_get_fault(flash); 184cb7a01acSMauro Carvalho Chehab if (IS_ERR_VALUE(rval)) 185cb7a01acSMauro Carvalho Chehab return rval; 186cb7a01acSMauro Carvalho Chehab if ((rval & (ADP1653_REG_FAULT_FLT_SCP | 187cb7a01acSMauro Carvalho Chehab ADP1653_REG_FAULT_FLT_OT | 188cb7a01acSMauro Carvalho Chehab ADP1653_REG_FAULT_FLT_OV)) && 189cb7a01acSMauro Carvalho Chehab (ctrl->id == V4L2_CID_FLASH_STROBE || 190cb7a01acSMauro Carvalho Chehab ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || 191cb7a01acSMauro Carvalho Chehab ctrl->id == V4L2_CID_FLASH_LED_MODE)) 192cb7a01acSMauro Carvalho Chehab return -EBUSY; 193cb7a01acSMauro Carvalho Chehab 194cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 195cb7a01acSMauro Carvalho Chehab case V4L2_CID_FLASH_STROBE: 196cb7a01acSMauro Carvalho Chehab return adp1653_strobe(flash, 1); 197cb7a01acSMauro Carvalho Chehab case V4L2_CID_FLASH_STROBE_STOP: 198cb7a01acSMauro Carvalho Chehab return adp1653_strobe(flash, 0); 199cb7a01acSMauro Carvalho Chehab } 200cb7a01acSMauro Carvalho Chehab 201cb7a01acSMauro Carvalho Chehab return adp1653_update_hw(flash); 202cb7a01acSMauro Carvalho Chehab } 203cb7a01acSMauro Carvalho Chehab 204cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { 205cb7a01acSMauro Carvalho Chehab .g_volatile_ctrl = adp1653_get_ctrl, 206cb7a01acSMauro Carvalho Chehab .s_ctrl = adp1653_set_ctrl, 207cb7a01acSMauro Carvalho Chehab }; 208cb7a01acSMauro Carvalho Chehab 209cb7a01acSMauro Carvalho Chehab static int adp1653_init_controls(struct adp1653_flash *flash) 210cb7a01acSMauro Carvalho Chehab { 211cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *fault; 212cb7a01acSMauro Carvalho Chehab 213cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_init(&flash->ctrls, 9); 214cb7a01acSMauro Carvalho Chehab 215cb7a01acSMauro Carvalho Chehab flash->led_mode = 216cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, 217cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_LED_MODE, 218cb7a01acSMauro Carvalho Chehab V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); 219cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, 220cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_STROBE_SOURCE, 221cb7a01acSMauro Carvalho Chehab V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); 222cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 223cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); 224cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 225cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); 226cb7a01acSMauro Carvalho Chehab flash->flash_timeout = 227cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 228cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, 229cb7a01acSMauro Carvalho Chehab flash->platform_data->max_flash_timeout, 230cb7a01acSMauro Carvalho Chehab TIMEOUT_STEP, 231cb7a01acSMauro Carvalho Chehab flash->platform_data->max_flash_timeout); 232cb7a01acSMauro Carvalho Chehab flash->flash_intensity = 233cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 234cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_INTENSITY, 235cb7a01acSMauro Carvalho Chehab ADP1653_FLASH_INTENSITY_MIN, 236cb7a01acSMauro Carvalho Chehab flash->platform_data->max_flash_intensity, 237cb7a01acSMauro Carvalho Chehab 1, flash->platform_data->max_flash_intensity); 238cb7a01acSMauro Carvalho Chehab flash->torch_intensity = 239cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 240cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_TORCH_INTENSITY, 241cb7a01acSMauro Carvalho Chehab ADP1653_TORCH_INTENSITY_MIN, 242cb7a01acSMauro Carvalho Chehab flash->platform_data->max_torch_intensity, 243cb7a01acSMauro Carvalho Chehab ADP1653_FLASH_INTENSITY_STEP, 244cb7a01acSMauro Carvalho Chehab flash->platform_data->max_torch_intensity); 245cb7a01acSMauro Carvalho Chehab flash->indicator_intensity = 246cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 247cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_INDICATOR_INTENSITY, 248cb7a01acSMauro Carvalho Chehab ADP1653_INDICATOR_INTENSITY_MIN, 249cb7a01acSMauro Carvalho Chehab flash->platform_data->max_indicator_intensity, 250cb7a01acSMauro Carvalho Chehab ADP1653_INDICATOR_INTENSITY_STEP, 251cb7a01acSMauro Carvalho Chehab ADP1653_INDICATOR_INTENSITY_MIN); 252cb7a01acSMauro Carvalho Chehab fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 253cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_FAULT, 0, 254cb7a01acSMauro Carvalho Chehab V4L2_FLASH_FAULT_OVER_VOLTAGE 255cb7a01acSMauro Carvalho Chehab | V4L2_FLASH_FAULT_OVER_TEMPERATURE 256cb7a01acSMauro Carvalho Chehab | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); 257cb7a01acSMauro Carvalho Chehab 258cb7a01acSMauro Carvalho Chehab if (flash->ctrls.error) 259cb7a01acSMauro Carvalho Chehab return flash->ctrls.error; 260cb7a01acSMauro Carvalho Chehab 261cb7a01acSMauro Carvalho Chehab fault->flags |= V4L2_CTRL_FLAG_VOLATILE; 262cb7a01acSMauro Carvalho Chehab 263cb7a01acSMauro Carvalho Chehab flash->subdev.ctrl_handler = &flash->ctrls; 264cb7a01acSMauro Carvalho Chehab return 0; 265cb7a01acSMauro Carvalho Chehab } 266cb7a01acSMauro Carvalho Chehab 267cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 268cb7a01acSMauro Carvalho Chehab * V4L2 subdev operations 269cb7a01acSMauro Carvalho Chehab */ 270cb7a01acSMauro Carvalho Chehab 271cb7a01acSMauro Carvalho Chehab static int 272cb7a01acSMauro Carvalho Chehab adp1653_init_device(struct adp1653_flash *flash) 273cb7a01acSMauro Carvalho Chehab { 274cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 275cb7a01acSMauro Carvalho Chehab int rval; 276cb7a01acSMauro Carvalho Chehab 277cb7a01acSMauro Carvalho Chehab /* Clear FAULT register by writing zero to OUT_SEL */ 278cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); 279cb7a01acSMauro Carvalho Chehab if (rval < 0) { 280cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "failed writing fault register\n"); 281cb7a01acSMauro Carvalho Chehab return -EIO; 282cb7a01acSMauro Carvalho Chehab } 283cb7a01acSMauro Carvalho Chehab 284cb7a01acSMauro Carvalho Chehab mutex_lock(flash->ctrls.lock); 285cb7a01acSMauro Carvalho Chehab /* Reset faults before reading new ones. */ 286cb7a01acSMauro Carvalho Chehab flash->fault = 0; 287cb7a01acSMauro Carvalho Chehab rval = adp1653_get_fault(flash); 288cb7a01acSMauro Carvalho Chehab mutex_unlock(flash->ctrls.lock); 289cb7a01acSMauro Carvalho Chehab if (rval > 0) { 290cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); 291cb7a01acSMauro Carvalho Chehab return -EIO; 292cb7a01acSMauro Carvalho Chehab } 293cb7a01acSMauro Carvalho Chehab 294cb7a01acSMauro Carvalho Chehab mutex_lock(flash->ctrls.lock); 295cb7a01acSMauro Carvalho Chehab rval = adp1653_update_hw(flash); 296cb7a01acSMauro Carvalho Chehab mutex_unlock(flash->ctrls.lock); 297cb7a01acSMauro Carvalho Chehab if (rval) { 298cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, 299cb7a01acSMauro Carvalho Chehab "adp1653_update_hw failed at %s\n", __func__); 300cb7a01acSMauro Carvalho Chehab return -EIO; 301cb7a01acSMauro Carvalho Chehab } 302cb7a01acSMauro Carvalho Chehab 303cb7a01acSMauro Carvalho Chehab return 0; 304cb7a01acSMauro Carvalho Chehab } 305cb7a01acSMauro Carvalho Chehab 306cb7a01acSMauro Carvalho Chehab static int 307cb7a01acSMauro Carvalho Chehab __adp1653_set_power(struct adp1653_flash *flash, int on) 308cb7a01acSMauro Carvalho Chehab { 309cb7a01acSMauro Carvalho Chehab int ret; 310cb7a01acSMauro Carvalho Chehab 311cb7a01acSMauro Carvalho Chehab ret = flash->platform_data->power(&flash->subdev, on); 312cb7a01acSMauro Carvalho Chehab if (ret < 0) 313cb7a01acSMauro Carvalho Chehab return ret; 314cb7a01acSMauro Carvalho Chehab 315cb7a01acSMauro Carvalho Chehab if (!on) 316cb7a01acSMauro Carvalho Chehab return 0; 317cb7a01acSMauro Carvalho Chehab 318cb7a01acSMauro Carvalho Chehab ret = adp1653_init_device(flash); 319cb7a01acSMauro Carvalho Chehab if (ret < 0) 320cb7a01acSMauro Carvalho Chehab flash->platform_data->power(&flash->subdev, 0); 321cb7a01acSMauro Carvalho Chehab 322cb7a01acSMauro Carvalho Chehab return ret; 323cb7a01acSMauro Carvalho Chehab } 324cb7a01acSMauro Carvalho Chehab 325cb7a01acSMauro Carvalho Chehab static int 326cb7a01acSMauro Carvalho Chehab adp1653_set_power(struct v4l2_subdev *subdev, int on) 327cb7a01acSMauro Carvalho Chehab { 328cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 329cb7a01acSMauro Carvalho Chehab int ret = 0; 330cb7a01acSMauro Carvalho Chehab 331cb7a01acSMauro Carvalho Chehab mutex_lock(&flash->power_lock); 332cb7a01acSMauro Carvalho Chehab 333cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0, 334cb7a01acSMauro Carvalho Chehab * update the power state. 335cb7a01acSMauro Carvalho Chehab */ 336cb7a01acSMauro Carvalho Chehab if (flash->power_count == !on) { 337cb7a01acSMauro Carvalho Chehab ret = __adp1653_set_power(flash, !!on); 338cb7a01acSMauro Carvalho Chehab if (ret < 0) 339cb7a01acSMauro Carvalho Chehab goto done; 340cb7a01acSMauro Carvalho Chehab } 341cb7a01acSMauro Carvalho Chehab 342cb7a01acSMauro Carvalho Chehab /* Update the power count. */ 343cb7a01acSMauro Carvalho Chehab flash->power_count += on ? 1 : -1; 344cb7a01acSMauro Carvalho Chehab WARN_ON(flash->power_count < 0); 345cb7a01acSMauro Carvalho Chehab 346cb7a01acSMauro Carvalho Chehab done: 347cb7a01acSMauro Carvalho Chehab mutex_unlock(&flash->power_lock); 348cb7a01acSMauro Carvalho Chehab return ret; 349cb7a01acSMauro Carvalho Chehab } 350cb7a01acSMauro Carvalho Chehab 351cb7a01acSMauro Carvalho Chehab static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 352cb7a01acSMauro Carvalho Chehab { 353cb7a01acSMauro Carvalho Chehab return adp1653_set_power(sd, 1); 354cb7a01acSMauro Carvalho Chehab } 355cb7a01acSMauro Carvalho Chehab 356cb7a01acSMauro Carvalho Chehab static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 357cb7a01acSMauro Carvalho Chehab { 358cb7a01acSMauro Carvalho Chehab return adp1653_set_power(sd, 0); 359cb7a01acSMauro Carvalho Chehab } 360cb7a01acSMauro Carvalho Chehab 361cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adp1653_core_ops = { 362cb7a01acSMauro Carvalho Chehab .s_power = adp1653_set_power, 363cb7a01acSMauro Carvalho Chehab }; 364cb7a01acSMauro Carvalho Chehab 365cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adp1653_ops = { 366cb7a01acSMauro Carvalho Chehab .core = &adp1653_core_ops, 367cb7a01acSMauro Carvalho Chehab }; 368cb7a01acSMauro Carvalho Chehab 369cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { 370cb7a01acSMauro Carvalho Chehab .open = adp1653_open, 371cb7a01acSMauro Carvalho Chehab .close = adp1653_close, 372cb7a01acSMauro Carvalho Chehab }; 373cb7a01acSMauro Carvalho Chehab 374cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 375cb7a01acSMauro Carvalho Chehab * I2C driver 376cb7a01acSMauro Carvalho Chehab */ 377cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_PM 378cb7a01acSMauro Carvalho Chehab 379cb7a01acSMauro Carvalho Chehab static int adp1653_suspend(struct device *dev) 380cb7a01acSMauro Carvalho Chehab { 381cb7a01acSMauro Carvalho Chehab struct i2c_client *client = to_i2c_client(dev); 382cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 383cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 384cb7a01acSMauro Carvalho Chehab 385cb7a01acSMauro Carvalho Chehab if (!flash->power_count) 386cb7a01acSMauro Carvalho Chehab return 0; 387cb7a01acSMauro Carvalho Chehab 388cb7a01acSMauro Carvalho Chehab return __adp1653_set_power(flash, 0); 389cb7a01acSMauro Carvalho Chehab } 390cb7a01acSMauro Carvalho Chehab 391cb7a01acSMauro Carvalho Chehab static int adp1653_resume(struct device *dev) 392cb7a01acSMauro Carvalho Chehab { 393cb7a01acSMauro Carvalho Chehab struct i2c_client *client = to_i2c_client(dev); 394cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 395cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 396cb7a01acSMauro Carvalho Chehab 397cb7a01acSMauro Carvalho Chehab if (!flash->power_count) 398cb7a01acSMauro Carvalho Chehab return 0; 399cb7a01acSMauro Carvalho Chehab 400cb7a01acSMauro Carvalho Chehab return __adp1653_set_power(flash, 1); 401cb7a01acSMauro Carvalho Chehab } 402cb7a01acSMauro Carvalho Chehab 403cb7a01acSMauro Carvalho Chehab #else 404cb7a01acSMauro Carvalho Chehab 405cb7a01acSMauro Carvalho Chehab #define adp1653_suspend NULL 406cb7a01acSMauro Carvalho Chehab #define adp1653_resume NULL 407cb7a01acSMauro Carvalho Chehab 408cb7a01acSMauro Carvalho Chehab #endif /* CONFIG_PM */ 409cb7a01acSMauro Carvalho Chehab 410cb7a01acSMauro Carvalho Chehab static int adp1653_probe(struct i2c_client *client, 411cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *devid) 412cb7a01acSMauro Carvalho Chehab { 413cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash; 414cb7a01acSMauro Carvalho Chehab int ret; 415cb7a01acSMauro Carvalho Chehab 416cb7a01acSMauro Carvalho Chehab /* we couldn't work without platform data */ 417cb7a01acSMauro Carvalho Chehab if (client->dev.platform_data == NULL) 418cb7a01acSMauro Carvalho Chehab return -ENODEV; 419cb7a01acSMauro Carvalho Chehab 420cb7a01acSMauro Carvalho Chehab flash = kzalloc(sizeof(*flash), GFP_KERNEL); 421cb7a01acSMauro Carvalho Chehab if (flash == NULL) 422cb7a01acSMauro Carvalho Chehab return -ENOMEM; 423cb7a01acSMauro Carvalho Chehab 424cb7a01acSMauro Carvalho Chehab flash->platform_data = client->dev.platform_data; 425cb7a01acSMauro Carvalho Chehab 426cb7a01acSMauro Carvalho Chehab mutex_init(&flash->power_lock); 427cb7a01acSMauro Carvalho Chehab 428cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); 429cb7a01acSMauro Carvalho Chehab flash->subdev.internal_ops = &adp1653_internal_ops; 430cb7a01acSMauro Carvalho Chehab flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 431cb7a01acSMauro Carvalho Chehab 432cb7a01acSMauro Carvalho Chehab ret = adp1653_init_controls(flash); 433cb7a01acSMauro Carvalho Chehab if (ret) 434cb7a01acSMauro Carvalho Chehab goto free_and_quit; 435cb7a01acSMauro Carvalho Chehab 436cb7a01acSMauro Carvalho Chehab ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); 437cb7a01acSMauro Carvalho Chehab if (ret < 0) 438cb7a01acSMauro Carvalho Chehab goto free_and_quit; 439cb7a01acSMauro Carvalho Chehab 440cb7a01acSMauro Carvalho Chehab flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; 441cb7a01acSMauro Carvalho Chehab 442cb7a01acSMauro Carvalho Chehab return 0; 443cb7a01acSMauro Carvalho Chehab 444cb7a01acSMauro Carvalho Chehab free_and_quit: 445cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&flash->ctrls); 446cb7a01acSMauro Carvalho Chehab kfree(flash); 447cb7a01acSMauro Carvalho Chehab return ret; 448cb7a01acSMauro Carvalho Chehab } 449cb7a01acSMauro Carvalho Chehab 450bf306900SDmitry Torokhov static int adp1653_remove(struct i2c_client *client) 451cb7a01acSMauro Carvalho Chehab { 452cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 453cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 454cb7a01acSMauro Carvalho Chehab 455cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(&flash->subdev); 456cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&flash->ctrls); 457cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&flash->subdev.entity); 458cb7a01acSMauro Carvalho Chehab kfree(flash); 459cb7a01acSMauro Carvalho Chehab return 0; 460cb7a01acSMauro Carvalho Chehab } 461cb7a01acSMauro Carvalho Chehab 462cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adp1653_id_table[] = { 463cb7a01acSMauro Carvalho Chehab { ADP1653_NAME, 0 }, 464cb7a01acSMauro Carvalho Chehab { } 465cb7a01acSMauro Carvalho Chehab }; 466cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adp1653_id_table); 467cb7a01acSMauro Carvalho Chehab 468cb7a01acSMauro Carvalho Chehab static struct dev_pm_ops adp1653_pm_ops = { 469cb7a01acSMauro Carvalho Chehab .suspend = adp1653_suspend, 470cb7a01acSMauro Carvalho Chehab .resume = adp1653_resume, 471cb7a01acSMauro Carvalho Chehab }; 472cb7a01acSMauro Carvalho Chehab 473cb7a01acSMauro Carvalho Chehab static struct i2c_driver adp1653_i2c_driver = { 474cb7a01acSMauro Carvalho Chehab .driver = { 475cb7a01acSMauro Carvalho Chehab .name = ADP1653_NAME, 476cb7a01acSMauro Carvalho Chehab .pm = &adp1653_pm_ops, 477cb7a01acSMauro Carvalho Chehab }, 478cb7a01acSMauro Carvalho Chehab .probe = adp1653_probe, 479bf306900SDmitry Torokhov .remove = adp1653_remove, 480cb7a01acSMauro Carvalho Chehab .id_table = adp1653_id_table, 481cb7a01acSMauro Carvalho Chehab }; 482cb7a01acSMauro Carvalho Chehab 483cb7a01acSMauro Carvalho Chehab module_i2c_driver(adp1653_i2c_driver); 484cb7a01acSMauro Carvalho Chehab 485cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); 486cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); 487cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 488