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> 11074c57a2SPavel Machek * Pavel Machek <pavel@ucw.cz> 12cb7a01acSMauro Carvalho Chehab * 13cb7a01acSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or 14cb7a01acSMauro Carvalho Chehab * modify it under the terms of the GNU General Public License 15cb7a01acSMauro Carvalho Chehab * version 2 as published by the Free Software Foundation. 16cb7a01acSMauro Carvalho Chehab * 17cb7a01acSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, but 18cb7a01acSMauro Carvalho Chehab * WITHOUT ANY WARRANTY; without even the implied warranty of 19cb7a01acSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20cb7a01acSMauro Carvalho Chehab * General Public License for more details. 21cb7a01acSMauro Carvalho Chehab * 22cb7a01acSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 23cb7a01acSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 24cb7a01acSMauro Carvalho Chehab * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 25cb7a01acSMauro Carvalho Chehab * 02110-1301 USA 26cb7a01acSMauro Carvalho Chehab * 27cb7a01acSMauro Carvalho Chehab * TODO: 28cb7a01acSMauro Carvalho Chehab * - fault interrupt handling 29cb7a01acSMauro Carvalho Chehab * - hardware strobe 30cb7a01acSMauro Carvalho Chehab * - power doesn't need to be ON if all lights are off 31cb7a01acSMauro Carvalho Chehab * 32cb7a01acSMauro Carvalho Chehab */ 33cb7a01acSMauro Carvalho Chehab 34cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 35cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 36cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 37cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 38074c57a2SPavel Machek #include <linux/of.h> 39074c57a2SPavel Machek #include <linux/gpio/consumer.h> 40b5dcee22SMauro Carvalho Chehab #include <media/i2c/adp1653.h> 41cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 42cb7a01acSMauro Carvalho Chehab 43cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MAX 820000 44cb7a01acSMauro Carvalho Chehab #define TIMEOUT_STEP 54600 45cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ 46cb7a01acSMauro Carvalho Chehab * TIMEOUT_STEP) 47cb7a01acSMauro Carvalho Chehab #define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ 48cb7a01acSMauro Carvalho Chehab / TIMEOUT_STEP) 49cb7a01acSMauro Carvalho Chehab #define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) 50cb7a01acSMauro Carvalho Chehab 51cb7a01acSMauro Carvalho Chehab /* Write values into ADP1653 registers. */ 52cb7a01acSMauro Carvalho Chehab static int adp1653_update_hw(struct adp1653_flash *flash) 53cb7a01acSMauro Carvalho Chehab { 54cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 55cb7a01acSMauro Carvalho Chehab u8 out_sel; 56cb7a01acSMauro Carvalho Chehab u8 config = 0; 57cb7a01acSMauro Carvalho Chehab int rval; 58cb7a01acSMauro Carvalho Chehab 59cb7a01acSMauro Carvalho Chehab out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( 60cb7a01acSMauro Carvalho Chehab flash->indicator_intensity->val) 61cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_ILED_SHIFT; 62cb7a01acSMauro Carvalho Chehab 63cb7a01acSMauro Carvalho Chehab switch (flash->led_mode->val) { 64cb7a01acSMauro Carvalho Chehab case V4L2_FLASH_LED_MODE_NONE: 65cb7a01acSMauro Carvalho Chehab break; 66cb7a01acSMauro Carvalho Chehab case V4L2_FLASH_LED_MODE_FLASH: 67cb7a01acSMauro Carvalho Chehab /* Flash mode, light on with strobe, duration from timer */ 68cb7a01acSMauro Carvalho Chehab config = ADP1653_REG_CONFIG_TMR_CFG; 69cb7a01acSMauro Carvalho Chehab config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) 70cb7a01acSMauro Carvalho Chehab << ADP1653_REG_CONFIG_TMR_SET_SHIFT; 71cb7a01acSMauro Carvalho Chehab break; 72cb7a01acSMauro Carvalho Chehab case V4L2_FLASH_LED_MODE_TORCH: 73cb7a01acSMauro Carvalho Chehab /* Torch mode, light immediately on, duration indefinite */ 74cb7a01acSMauro Carvalho Chehab out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( 75cb7a01acSMauro Carvalho Chehab flash->torch_intensity->val) 76cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_HPLED_SHIFT; 77cb7a01acSMauro Carvalho Chehab break; 78cb7a01acSMauro Carvalho Chehab } 79cb7a01acSMauro Carvalho Chehab 80cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); 81cb7a01acSMauro Carvalho Chehab if (rval < 0) 82cb7a01acSMauro Carvalho Chehab return rval; 83cb7a01acSMauro Carvalho Chehab 84cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); 85cb7a01acSMauro Carvalho Chehab if (rval < 0) 86cb7a01acSMauro Carvalho Chehab return rval; 87cb7a01acSMauro Carvalho Chehab 88cb7a01acSMauro Carvalho Chehab return 0; 89cb7a01acSMauro Carvalho Chehab } 90cb7a01acSMauro Carvalho Chehab 91cb7a01acSMauro Carvalho Chehab static int adp1653_get_fault(struct adp1653_flash *flash) 92cb7a01acSMauro Carvalho Chehab { 93cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 94cb7a01acSMauro Carvalho Chehab int fault; 95cb7a01acSMauro Carvalho Chehab int rval; 96cb7a01acSMauro Carvalho Chehab 97cb7a01acSMauro Carvalho Chehab fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); 98287980e4SArnd Bergmann if (fault < 0) 99cb7a01acSMauro Carvalho Chehab return fault; 100cb7a01acSMauro Carvalho Chehab 101cb7a01acSMauro Carvalho Chehab flash->fault |= fault; 102cb7a01acSMauro Carvalho Chehab 103cb7a01acSMauro Carvalho Chehab if (!flash->fault) 104cb7a01acSMauro Carvalho Chehab return 0; 105cb7a01acSMauro Carvalho Chehab 106cb7a01acSMauro Carvalho Chehab /* Clear faults. */ 107cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); 108287980e4SArnd Bergmann if (rval < 0) 109cb7a01acSMauro Carvalho Chehab return rval; 110cb7a01acSMauro Carvalho Chehab 111cb7a01acSMauro Carvalho Chehab flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; 112cb7a01acSMauro Carvalho Chehab 113cb7a01acSMauro Carvalho Chehab rval = adp1653_update_hw(flash); 114287980e4SArnd Bergmann if (rval) 115cb7a01acSMauro Carvalho Chehab return rval; 116cb7a01acSMauro Carvalho Chehab 117cb7a01acSMauro Carvalho Chehab return flash->fault; 118cb7a01acSMauro Carvalho Chehab } 119cb7a01acSMauro Carvalho Chehab 120cb7a01acSMauro Carvalho Chehab static int adp1653_strobe(struct adp1653_flash *flash, int enable) 121cb7a01acSMauro Carvalho Chehab { 122cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 123cb7a01acSMauro Carvalho Chehab u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( 124cb7a01acSMauro Carvalho Chehab flash->indicator_intensity->val) 125cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_ILED_SHIFT; 126cb7a01acSMauro Carvalho Chehab int rval; 127cb7a01acSMauro Carvalho Chehab 128cb7a01acSMauro Carvalho Chehab if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) 129cb7a01acSMauro Carvalho Chehab return -EBUSY; 130cb7a01acSMauro Carvalho Chehab 131cb7a01acSMauro Carvalho Chehab if (!enable) 132cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 133cb7a01acSMauro Carvalho Chehab out_sel); 134cb7a01acSMauro Carvalho Chehab 135cb7a01acSMauro Carvalho Chehab out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( 136cb7a01acSMauro Carvalho Chehab flash->flash_intensity->val) 137cb7a01acSMauro Carvalho Chehab << ADP1653_REG_OUT_SEL_HPLED_SHIFT; 138cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); 139cb7a01acSMauro Carvalho Chehab if (rval) 140cb7a01acSMauro Carvalho Chehab return rval; 141cb7a01acSMauro Carvalho Chehab 142cb7a01acSMauro Carvalho Chehab /* Software strobe using i2c */ 143cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 144cb7a01acSMauro Carvalho Chehab ADP1653_REG_SW_STROBE_SW_STROBE); 145cb7a01acSMauro Carvalho Chehab if (rval) 146cb7a01acSMauro Carvalho Chehab return rval; 147cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); 148cb7a01acSMauro Carvalho Chehab } 149cb7a01acSMauro Carvalho Chehab 150cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 151cb7a01acSMauro Carvalho Chehab * V4L2 controls 152cb7a01acSMauro Carvalho Chehab */ 153cb7a01acSMauro Carvalho Chehab 154cb7a01acSMauro Carvalho Chehab static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) 155cb7a01acSMauro Carvalho Chehab { 156cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = 157cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct adp1653_flash, ctrls); 158cb7a01acSMauro Carvalho Chehab int rval; 159cb7a01acSMauro Carvalho Chehab 160cb7a01acSMauro Carvalho Chehab rval = adp1653_get_fault(flash); 161287980e4SArnd Bergmann if (rval) 162cb7a01acSMauro Carvalho Chehab return rval; 163cb7a01acSMauro Carvalho Chehab 164cb7a01acSMauro Carvalho Chehab ctrl->cur.val = 0; 165cb7a01acSMauro Carvalho Chehab 166cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) 167cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; 168cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_OT) 169cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; 170cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) 171cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; 172cb7a01acSMauro Carvalho Chehab if (flash->fault & ADP1653_REG_FAULT_FLT_OV) 173cb7a01acSMauro Carvalho Chehab ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; 174cb7a01acSMauro Carvalho Chehab 175cb7a01acSMauro Carvalho Chehab flash->fault = 0; 176cb7a01acSMauro Carvalho Chehab 177cb7a01acSMauro Carvalho Chehab return 0; 178cb7a01acSMauro Carvalho Chehab } 179cb7a01acSMauro Carvalho Chehab 180cb7a01acSMauro Carvalho Chehab static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) 181cb7a01acSMauro Carvalho Chehab { 182cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = 183cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct adp1653_flash, ctrls); 184cb7a01acSMauro Carvalho Chehab int rval; 185cb7a01acSMauro Carvalho Chehab 186cb7a01acSMauro Carvalho Chehab rval = adp1653_get_fault(flash); 187287980e4SArnd Bergmann if (rval) 188cb7a01acSMauro Carvalho Chehab return rval; 189cb7a01acSMauro Carvalho Chehab if ((rval & (ADP1653_REG_FAULT_FLT_SCP | 190cb7a01acSMauro Carvalho Chehab ADP1653_REG_FAULT_FLT_OT | 191cb7a01acSMauro Carvalho Chehab ADP1653_REG_FAULT_FLT_OV)) && 192cb7a01acSMauro Carvalho Chehab (ctrl->id == V4L2_CID_FLASH_STROBE || 193cb7a01acSMauro Carvalho Chehab ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || 194cb7a01acSMauro Carvalho Chehab ctrl->id == V4L2_CID_FLASH_LED_MODE)) 195cb7a01acSMauro Carvalho Chehab return -EBUSY; 196cb7a01acSMauro Carvalho Chehab 197cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 198cb7a01acSMauro Carvalho Chehab case V4L2_CID_FLASH_STROBE: 199cb7a01acSMauro Carvalho Chehab return adp1653_strobe(flash, 1); 200cb7a01acSMauro Carvalho Chehab case V4L2_CID_FLASH_STROBE_STOP: 201cb7a01acSMauro Carvalho Chehab return adp1653_strobe(flash, 0); 202cb7a01acSMauro Carvalho Chehab } 203cb7a01acSMauro Carvalho Chehab 204cb7a01acSMauro Carvalho Chehab return adp1653_update_hw(flash); 205cb7a01acSMauro Carvalho Chehab } 206cb7a01acSMauro Carvalho Chehab 207cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { 208cb7a01acSMauro Carvalho Chehab .g_volatile_ctrl = adp1653_get_ctrl, 209cb7a01acSMauro Carvalho Chehab .s_ctrl = adp1653_set_ctrl, 210cb7a01acSMauro Carvalho Chehab }; 211cb7a01acSMauro Carvalho Chehab 212cb7a01acSMauro Carvalho Chehab static int adp1653_init_controls(struct adp1653_flash *flash) 213cb7a01acSMauro Carvalho Chehab { 214cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *fault; 215cb7a01acSMauro Carvalho Chehab 216cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_init(&flash->ctrls, 9); 217cb7a01acSMauro Carvalho Chehab 218cb7a01acSMauro Carvalho Chehab flash->led_mode = 219cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, 220cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_LED_MODE, 221cb7a01acSMauro Carvalho Chehab V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); 222cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, 223cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_STROBE_SOURCE, 224cb7a01acSMauro Carvalho Chehab V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); 225cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 226cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); 227cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 228cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); 229cb7a01acSMauro Carvalho Chehab flash->flash_timeout = 230cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 231cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, 232cb7a01acSMauro Carvalho Chehab flash->platform_data->max_flash_timeout, 233cb7a01acSMauro Carvalho Chehab TIMEOUT_STEP, 234cb7a01acSMauro Carvalho Chehab flash->platform_data->max_flash_timeout); 235cb7a01acSMauro Carvalho Chehab flash->flash_intensity = 236cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 237cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_INTENSITY, 238cb7a01acSMauro Carvalho Chehab ADP1653_FLASH_INTENSITY_MIN, 239cb7a01acSMauro Carvalho Chehab flash->platform_data->max_flash_intensity, 240cb7a01acSMauro Carvalho Chehab 1, flash->platform_data->max_flash_intensity); 241cb7a01acSMauro Carvalho Chehab flash->torch_intensity = 242cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 243cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_TORCH_INTENSITY, 244cb7a01acSMauro Carvalho Chehab ADP1653_TORCH_INTENSITY_MIN, 245cb7a01acSMauro Carvalho Chehab flash->platform_data->max_torch_intensity, 246cb7a01acSMauro Carvalho Chehab ADP1653_FLASH_INTENSITY_STEP, 247cb7a01acSMauro Carvalho Chehab flash->platform_data->max_torch_intensity); 248cb7a01acSMauro Carvalho Chehab flash->indicator_intensity = 249cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 250cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_INDICATOR_INTENSITY, 251cb7a01acSMauro Carvalho Chehab ADP1653_INDICATOR_INTENSITY_MIN, 252cb7a01acSMauro Carvalho Chehab flash->platform_data->max_indicator_intensity, 253cb7a01acSMauro Carvalho Chehab ADP1653_INDICATOR_INTENSITY_STEP, 254cb7a01acSMauro Carvalho Chehab ADP1653_INDICATOR_INTENSITY_MIN); 255cb7a01acSMauro Carvalho Chehab fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, 256cb7a01acSMauro Carvalho Chehab V4L2_CID_FLASH_FAULT, 0, 257cb7a01acSMauro Carvalho Chehab V4L2_FLASH_FAULT_OVER_VOLTAGE 258cb7a01acSMauro Carvalho Chehab | V4L2_FLASH_FAULT_OVER_TEMPERATURE 259cb7a01acSMauro Carvalho Chehab | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); 260cb7a01acSMauro Carvalho Chehab 261cb7a01acSMauro Carvalho Chehab if (flash->ctrls.error) 262cb7a01acSMauro Carvalho Chehab return flash->ctrls.error; 263cb7a01acSMauro Carvalho Chehab 264cb7a01acSMauro Carvalho Chehab fault->flags |= V4L2_CTRL_FLAG_VOLATILE; 265cb7a01acSMauro Carvalho Chehab 266cb7a01acSMauro Carvalho Chehab flash->subdev.ctrl_handler = &flash->ctrls; 267cb7a01acSMauro Carvalho Chehab return 0; 268cb7a01acSMauro Carvalho Chehab } 269cb7a01acSMauro Carvalho Chehab 270cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 271cb7a01acSMauro Carvalho Chehab * V4L2 subdev operations 272cb7a01acSMauro Carvalho Chehab */ 273cb7a01acSMauro Carvalho Chehab 274cb7a01acSMauro Carvalho Chehab static int 275cb7a01acSMauro Carvalho Chehab adp1653_init_device(struct adp1653_flash *flash) 276cb7a01acSMauro Carvalho Chehab { 277cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); 278cb7a01acSMauro Carvalho Chehab int rval; 279cb7a01acSMauro Carvalho Chehab 280cb7a01acSMauro Carvalho Chehab /* Clear FAULT register by writing zero to OUT_SEL */ 281cb7a01acSMauro Carvalho Chehab rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); 282cb7a01acSMauro Carvalho Chehab if (rval < 0) { 283cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "failed writing fault register\n"); 284cb7a01acSMauro Carvalho Chehab return -EIO; 285cb7a01acSMauro Carvalho Chehab } 286cb7a01acSMauro Carvalho Chehab 287cb7a01acSMauro Carvalho Chehab mutex_lock(flash->ctrls.lock); 288cb7a01acSMauro Carvalho Chehab /* Reset faults before reading new ones. */ 289cb7a01acSMauro Carvalho Chehab flash->fault = 0; 290cb7a01acSMauro Carvalho Chehab rval = adp1653_get_fault(flash); 291cb7a01acSMauro Carvalho Chehab mutex_unlock(flash->ctrls.lock); 292cb7a01acSMauro Carvalho Chehab if (rval > 0) { 293cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); 294cb7a01acSMauro Carvalho Chehab return -EIO; 295cb7a01acSMauro Carvalho Chehab } 296cb7a01acSMauro Carvalho Chehab 297cb7a01acSMauro Carvalho Chehab mutex_lock(flash->ctrls.lock); 298cb7a01acSMauro Carvalho Chehab rval = adp1653_update_hw(flash); 299cb7a01acSMauro Carvalho Chehab mutex_unlock(flash->ctrls.lock); 300cb7a01acSMauro Carvalho Chehab if (rval) { 301cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, 302cb7a01acSMauro Carvalho Chehab "adp1653_update_hw failed at %s\n", __func__); 303cb7a01acSMauro Carvalho Chehab return -EIO; 304cb7a01acSMauro Carvalho Chehab } 305cb7a01acSMauro Carvalho Chehab 306cb7a01acSMauro Carvalho Chehab return 0; 307cb7a01acSMauro Carvalho Chehab } 308cb7a01acSMauro Carvalho Chehab 309cb7a01acSMauro Carvalho Chehab static int 310cb7a01acSMauro Carvalho Chehab __adp1653_set_power(struct adp1653_flash *flash, int on) 311cb7a01acSMauro Carvalho Chehab { 312be8e58d9SMauro Carvalho Chehab int ret; 313cb7a01acSMauro Carvalho Chehab 314074c57a2SPavel Machek if (flash->platform_data->power) { 315cb7a01acSMauro Carvalho Chehab ret = flash->platform_data->power(&flash->subdev, on); 316cb7a01acSMauro Carvalho Chehab if (ret < 0) 317cb7a01acSMauro Carvalho Chehab return ret; 318074c57a2SPavel Machek } else { 319074c57a2SPavel Machek gpiod_set_value(flash->platform_data->enable_gpio, on); 320074c57a2SPavel Machek if (on) 321074c57a2SPavel Machek /* Some delay is apparently required. */ 322074c57a2SPavel Machek udelay(20); 323074c57a2SPavel Machek } 324cb7a01acSMauro Carvalho Chehab 325cb7a01acSMauro Carvalho Chehab if (!on) 326cb7a01acSMauro Carvalho Chehab return 0; 327cb7a01acSMauro Carvalho Chehab 328cb7a01acSMauro Carvalho Chehab ret = adp1653_init_device(flash); 329074c57a2SPavel Machek if (ret >= 0) 330074c57a2SPavel Machek return ret; 331074c57a2SPavel Machek 332074c57a2SPavel Machek if (flash->platform_data->power) 333cb7a01acSMauro Carvalho Chehab flash->platform_data->power(&flash->subdev, 0); 334074c57a2SPavel Machek else 335074c57a2SPavel Machek gpiod_set_value(flash->platform_data->enable_gpio, 0); 336cb7a01acSMauro Carvalho Chehab 337cb7a01acSMauro Carvalho Chehab return ret; 338cb7a01acSMauro Carvalho Chehab } 339cb7a01acSMauro Carvalho Chehab 340cb7a01acSMauro Carvalho Chehab static int 341cb7a01acSMauro Carvalho Chehab adp1653_set_power(struct v4l2_subdev *subdev, int on) 342cb7a01acSMauro Carvalho Chehab { 343cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 344cb7a01acSMauro Carvalho Chehab int ret = 0; 345cb7a01acSMauro Carvalho Chehab 346cb7a01acSMauro Carvalho Chehab mutex_lock(&flash->power_lock); 347cb7a01acSMauro Carvalho Chehab 348cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0, 349cb7a01acSMauro Carvalho Chehab * update the power state. 350cb7a01acSMauro Carvalho Chehab */ 351cb7a01acSMauro Carvalho Chehab if (flash->power_count == !on) { 352cb7a01acSMauro Carvalho Chehab ret = __adp1653_set_power(flash, !!on); 353cb7a01acSMauro Carvalho Chehab if (ret < 0) 354cb7a01acSMauro Carvalho Chehab goto done; 355cb7a01acSMauro Carvalho Chehab } 356cb7a01acSMauro Carvalho Chehab 357cb7a01acSMauro Carvalho Chehab /* Update the power count. */ 358cb7a01acSMauro Carvalho Chehab flash->power_count += on ? 1 : -1; 359cb7a01acSMauro Carvalho Chehab WARN_ON(flash->power_count < 0); 360cb7a01acSMauro Carvalho Chehab 361cb7a01acSMauro Carvalho Chehab done: 362cb7a01acSMauro Carvalho Chehab mutex_unlock(&flash->power_lock); 363cb7a01acSMauro Carvalho Chehab return ret; 364cb7a01acSMauro Carvalho Chehab } 365cb7a01acSMauro Carvalho Chehab 366cb7a01acSMauro Carvalho Chehab static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 367cb7a01acSMauro Carvalho Chehab { 368cb7a01acSMauro Carvalho Chehab return adp1653_set_power(sd, 1); 369cb7a01acSMauro Carvalho Chehab } 370cb7a01acSMauro Carvalho Chehab 371cb7a01acSMauro Carvalho Chehab static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 372cb7a01acSMauro Carvalho Chehab { 373cb7a01acSMauro Carvalho Chehab return adp1653_set_power(sd, 0); 374cb7a01acSMauro Carvalho Chehab } 375cb7a01acSMauro Carvalho Chehab 376cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adp1653_core_ops = { 377cb7a01acSMauro Carvalho Chehab .s_power = adp1653_set_power, 378cb7a01acSMauro Carvalho Chehab }; 379cb7a01acSMauro Carvalho Chehab 380cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adp1653_ops = { 381cb7a01acSMauro Carvalho Chehab .core = &adp1653_core_ops, 382cb7a01acSMauro Carvalho Chehab }; 383cb7a01acSMauro Carvalho Chehab 384cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { 385cb7a01acSMauro Carvalho Chehab .open = adp1653_open, 386cb7a01acSMauro Carvalho Chehab .close = adp1653_close, 387cb7a01acSMauro Carvalho Chehab }; 388cb7a01acSMauro Carvalho Chehab 389cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 390cb7a01acSMauro Carvalho Chehab * I2C driver 391cb7a01acSMauro Carvalho Chehab */ 392cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_PM 393cb7a01acSMauro Carvalho Chehab 394cb7a01acSMauro Carvalho Chehab static int adp1653_suspend(struct device *dev) 395cb7a01acSMauro Carvalho Chehab { 396cb7a01acSMauro Carvalho Chehab struct i2c_client *client = to_i2c_client(dev); 397cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 398cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 399cb7a01acSMauro Carvalho Chehab 400cb7a01acSMauro Carvalho Chehab if (!flash->power_count) 401cb7a01acSMauro Carvalho Chehab return 0; 402cb7a01acSMauro Carvalho Chehab 403cb7a01acSMauro Carvalho Chehab return __adp1653_set_power(flash, 0); 404cb7a01acSMauro Carvalho Chehab } 405cb7a01acSMauro Carvalho Chehab 406cb7a01acSMauro Carvalho Chehab static int adp1653_resume(struct device *dev) 407cb7a01acSMauro Carvalho Chehab { 408cb7a01acSMauro Carvalho Chehab struct i2c_client *client = to_i2c_client(dev); 409cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 410cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 411cb7a01acSMauro Carvalho Chehab 412cb7a01acSMauro Carvalho Chehab if (!flash->power_count) 413cb7a01acSMauro Carvalho Chehab return 0; 414cb7a01acSMauro Carvalho Chehab 415cb7a01acSMauro Carvalho Chehab return __adp1653_set_power(flash, 1); 416cb7a01acSMauro Carvalho Chehab } 417cb7a01acSMauro Carvalho Chehab 418cb7a01acSMauro Carvalho Chehab #else 419cb7a01acSMauro Carvalho Chehab 420cb7a01acSMauro Carvalho Chehab #define adp1653_suspend NULL 421cb7a01acSMauro Carvalho Chehab #define adp1653_resume NULL 422cb7a01acSMauro Carvalho Chehab 423cb7a01acSMauro Carvalho Chehab #endif /* CONFIG_PM */ 424cb7a01acSMauro Carvalho Chehab 425074c57a2SPavel Machek static int adp1653_of_init(struct i2c_client *client, 426074c57a2SPavel Machek struct adp1653_flash *flash, 427074c57a2SPavel Machek struct device_node *node) 428074c57a2SPavel Machek { 429074c57a2SPavel Machek struct adp1653_platform_data *pd; 430074c57a2SPavel Machek struct device_node *child; 431074c57a2SPavel Machek 432074c57a2SPavel Machek pd = devm_kzalloc(&client->dev, sizeof(*pd), GFP_KERNEL); 433074c57a2SPavel Machek if (!pd) 434074c57a2SPavel Machek return -ENOMEM; 435074c57a2SPavel Machek flash->platform_data = pd; 436074c57a2SPavel Machek 437074c57a2SPavel Machek child = of_get_child_by_name(node, "flash"); 438074c57a2SPavel Machek if (!child) 439074c57a2SPavel Machek return -EINVAL; 440074c57a2SPavel Machek 441074c57a2SPavel Machek if (of_property_read_u32(child, "flash-timeout-us", 442074c57a2SPavel Machek &pd->max_flash_timeout)) 443074c57a2SPavel Machek goto err; 444074c57a2SPavel Machek 445074c57a2SPavel Machek if (of_property_read_u32(child, "flash-max-microamp", 446074c57a2SPavel Machek &pd->max_flash_intensity)) 447074c57a2SPavel Machek goto err; 448074c57a2SPavel Machek 449074c57a2SPavel Machek pd->max_flash_intensity /= 1000; 450074c57a2SPavel Machek 451074c57a2SPavel Machek if (of_property_read_u32(child, "led-max-microamp", 452074c57a2SPavel Machek &pd->max_torch_intensity)) 453074c57a2SPavel Machek goto err; 454074c57a2SPavel Machek 455074c57a2SPavel Machek pd->max_torch_intensity /= 1000; 456074c57a2SPavel Machek of_node_put(child); 457074c57a2SPavel Machek 458074c57a2SPavel Machek child = of_get_child_by_name(node, "indicator"); 459074c57a2SPavel Machek if (!child) 460074c57a2SPavel Machek return -EINVAL; 461074c57a2SPavel Machek 462074c57a2SPavel Machek if (of_property_read_u32(child, "led-max-microamp", 463074c57a2SPavel Machek &pd->max_indicator_intensity)) 464074c57a2SPavel Machek goto err; 465074c57a2SPavel Machek 466074c57a2SPavel Machek of_node_put(child); 467074c57a2SPavel Machek 468a33c380eSUwe Kleine-König pd->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW); 469806f8ffaSVladimir Zapolskiy if (IS_ERR(pd->enable_gpio)) { 470074c57a2SPavel Machek dev_err(&client->dev, "Error getting GPIO\n"); 471806f8ffaSVladimir Zapolskiy return PTR_ERR(pd->enable_gpio); 472074c57a2SPavel Machek } 473074c57a2SPavel Machek 474074c57a2SPavel Machek return 0; 475074c57a2SPavel Machek err: 476074c57a2SPavel Machek dev_err(&client->dev, "Required property not found\n"); 477074c57a2SPavel Machek of_node_put(child); 478074c57a2SPavel Machek return -EINVAL; 479074c57a2SPavel Machek } 480074c57a2SPavel Machek 481074c57a2SPavel Machek 482cb7a01acSMauro Carvalho Chehab static int adp1653_probe(struct i2c_client *client, 483cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *devid) 484cb7a01acSMauro Carvalho Chehab { 485cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash; 486cb7a01acSMauro Carvalho Chehab int ret; 487cb7a01acSMauro Carvalho Chehab 488c02b211dSLaurent Pinchart flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); 489cb7a01acSMauro Carvalho Chehab if (flash == NULL) 490cb7a01acSMauro Carvalho Chehab return -ENOMEM; 491cb7a01acSMauro Carvalho Chehab 492074c57a2SPavel Machek if (client->dev.of_node) { 493074c57a2SPavel Machek ret = adp1653_of_init(client, flash, client->dev.of_node); 494074c57a2SPavel Machek if (ret) 495074c57a2SPavel Machek return ret; 496074c57a2SPavel Machek } else { 497074c57a2SPavel Machek if (!client->dev.platform_data) { 498074c57a2SPavel Machek dev_err(&client->dev, 499074c57a2SPavel Machek "Neither DT not platform data provided\n"); 500b888d232SAnton Protopopov return -EINVAL; 501074c57a2SPavel Machek } 502cb7a01acSMauro Carvalho Chehab flash->platform_data = client->dev.platform_data; 503074c57a2SPavel Machek } 504cb7a01acSMauro Carvalho Chehab 505cb7a01acSMauro Carvalho Chehab mutex_init(&flash->power_lock); 506cb7a01acSMauro Carvalho Chehab 507cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); 508cb7a01acSMauro Carvalho Chehab flash->subdev.internal_ops = &adp1653_internal_ops; 509cb7a01acSMauro Carvalho Chehab flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 510cb7a01acSMauro Carvalho Chehab 511cb7a01acSMauro Carvalho Chehab ret = adp1653_init_controls(flash); 512cb7a01acSMauro Carvalho Chehab if (ret) 513cb7a01acSMauro Carvalho Chehab goto free_and_quit; 514cb7a01acSMauro Carvalho Chehab 515ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL); 516cb7a01acSMauro Carvalho Chehab if (ret < 0) 517cb7a01acSMauro Carvalho Chehab goto free_and_quit; 518cb7a01acSMauro Carvalho Chehab 5194ca72efaSMauro Carvalho Chehab flash->subdev.entity.function = MEDIA_ENT_F_FLASH; 520be8e58d9SMauro Carvalho Chehab 521cb7a01acSMauro Carvalho Chehab return 0; 522cb7a01acSMauro Carvalho Chehab 523cb7a01acSMauro Carvalho Chehab free_and_quit: 524074c57a2SPavel Machek dev_err(&client->dev, "adp1653: failed to register device\n"); 525cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&flash->ctrls); 526cb7a01acSMauro Carvalho Chehab return ret; 527cb7a01acSMauro Carvalho Chehab } 528cb7a01acSMauro Carvalho Chehab 529bf306900SDmitry Torokhov static int adp1653_remove(struct i2c_client *client) 530cb7a01acSMauro Carvalho Chehab { 531cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 532cb7a01acSMauro Carvalho Chehab struct adp1653_flash *flash = to_adp1653_flash(subdev); 533cb7a01acSMauro Carvalho Chehab 534cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(&flash->subdev); 535cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&flash->ctrls); 536cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&flash->subdev.entity); 537c02b211dSLaurent Pinchart 538cb7a01acSMauro Carvalho Chehab return 0; 539cb7a01acSMauro Carvalho Chehab } 540cb7a01acSMauro Carvalho Chehab 541cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adp1653_id_table[] = { 542cb7a01acSMauro Carvalho Chehab { ADP1653_NAME, 0 }, 543cb7a01acSMauro Carvalho Chehab { } 544cb7a01acSMauro Carvalho Chehab }; 545cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adp1653_id_table); 546cb7a01acSMauro Carvalho Chehab 547074c57a2SPavel Machek static const struct dev_pm_ops adp1653_pm_ops = { 548cb7a01acSMauro Carvalho Chehab .suspend = adp1653_suspend, 549cb7a01acSMauro Carvalho Chehab .resume = adp1653_resume, 550cb7a01acSMauro Carvalho Chehab }; 551cb7a01acSMauro Carvalho Chehab 552cb7a01acSMauro Carvalho Chehab static struct i2c_driver adp1653_i2c_driver = { 553cb7a01acSMauro Carvalho Chehab .driver = { 554cb7a01acSMauro Carvalho Chehab .name = ADP1653_NAME, 555cb7a01acSMauro Carvalho Chehab .pm = &adp1653_pm_ops, 556cb7a01acSMauro Carvalho Chehab }, 557cb7a01acSMauro Carvalho Chehab .probe = adp1653_probe, 558bf306900SDmitry Torokhov .remove = adp1653_remove, 559cb7a01acSMauro Carvalho Chehab .id_table = adp1653_id_table, 560cb7a01acSMauro Carvalho Chehab }; 561cb7a01acSMauro Carvalho Chehab 562cb7a01acSMauro Carvalho Chehab module_i2c_driver(adp1653_i2c_driver); 563cb7a01acSMauro Carvalho Chehab 564cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); 565cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); 566cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 567