diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/Kconfig | 4 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/camera_flash/Makefile | 5 | ||||
-rw-r--r-- | drivers/staging/camera_flash/adp1653.c | 595 | ||||
-rwxr-xr-x | drivers/staging/camera_flash/adp1653.h | 82 | ||||
-rw-r--r-- | drivers/staging/camera_flash/flash_common.c | 459 | ||||
-rwxr-xr-x | drivers/staging/camera_flash/flash_common.h | 56 |
7 files changed, 1202 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 8d7ebc30e12..08edeb055d7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -28,6 +28,10 @@ config U8500_MMIO bool "ST-Ericsson MMIO (Camera) Driver" depends on ARCH_U8500 +config U8500_FLASH + bool "ST-Ericsson Flash (Camera) Driver" + depends on ARCH_U8500 + config U8500_CM tristate "U8500 Component Manager driver" depends on UX500_SOC_DB8500 diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 5917ede01fe..6c0f4bd21cf 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -73,4 +73,5 @@ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_CW1200) += cw1200/ obj-$(CONFIG_U8500_MMIO) += mmio/ +obj-$(CONFIG_U8500_FLASH) += camera_flash/ obj-$(CONFIG_U8500_CM) += nmf-cm/ diff --git a/drivers/staging/camera_flash/Makefile b/drivers/staging/camera_flash/Makefile new file mode 100644 index 00000000000..bf2f5aa2dd3 --- /dev/null +++ b/drivers/staging/camera_flash/Makefile @@ -0,0 +1,5 @@ +export ADP1653_SUPPORT +EXTRA_CFLAGS += -DADP1653_SUPPORT +obj-$(CONFIG_U8500_FLASH) := camera_flash.o +camera_flash-y := flash_common.o +camera_flash-y += adp1653.o diff --git a/drivers/staging/camera_flash/adp1653.c b/drivers/staging/camera_flash/adp1653.c new file mode 100644 index 00000000000..e508ef072d8 --- /dev/null +++ b/drivers/staging/camera_flash/adp1653.c @@ -0,0 +1,595 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * adp1653: Driver Adp1653 HPLED flash driver chip. This driver + * currently support I2C interface, 2bit interface is not supported. + * Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <linux/camera_flash.h> +#include <asm/mach-types.h> +#include "flash_common.h" +#include "adp1653.h" + +/* This data is platform specific for 8500 href-v1 platform, + * Ideally this should be supplied from platform code + */ +//#define ADAPTER_I2C2 (2) +#define STR_GPIO (6) + +static int strobe_gpio = 0; //TODO: set to 0 when it works +static int adapter_i2c2 = 2; +static int flash_irq = 7; +static int enable_gpio = 272; +static int flash_position = 0; +module_param(strobe_gpio, int, S_IRUGO); +MODULE_PARM_DESC(strobe_gpio, "use GPIO 6 to strobe, 0 means that someone else (sensor?) will strobe for us"); +module_param(adapter_i2c2, int, S_IRUGO); +MODULE_PARM_DESC(adapter_i2c2, "use the given I2C adaptater to communicate with the chip"); +module_param(flash_irq, int, S_IRUGO); +MODULE_PARM_DESC(flash_irq, "the GPIO number associated to the i2c irq line"); +module_param(enable_gpio, int, S_IRUGO); +MODULE_PARM_DESC(enable_gpio, "use the given GPIO line to enable the chip"); +module_param(flash_position, int, S_IRUGO); +MODULE_PARM_DESC(flash_position, "the position of the flash chip (0=PRIMARY, 1=SECONDARY)"); + + +int __flash_gpio_to_irq(int gpio) +{ + + return NOMADIK_GPIO_TO_IRQ(gpio); +} + +#define DEBUG_LOG(...) printk(KERN_DEBUG "Adp1653 flash driver: " __VA_ARGS__) + +#define ADP1653_SUPPORTED_MODES (FLASH_MODE_VIDEO_LED | FLASH_MODE_STILL_LED | \ + FLASH_MODE_STILL_LED_EXTERNAL_STROBE | \ + FLASH_MODE_AF_ASSISTANT | FLASH_MODE_INDICATOR) + +#define ADP1653_SELFTEST_SUPPORTED_MODES (FLASH_SELFTEST_CONNECTION | FLASH_SELFTEST_FLASH_WITH_STROBE | \ + FLASH_SELFTEST_VIDEO_LIGHT | FLASH_SELFTEST_AF_LIGHT | FLASH_SELFTEST_INDICATOR | FLASH_SELFTEST_TORCH_LIGHT) + +static int adp1653_trigger_strobe(void *priv_data, int enable); + +static int adp1653_get_modes(void *priv_data,unsigned long *modes) +{ + int err; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + err = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG); + if (err) + *modes = 0x0; + else + *modes = ADP1653_SUPPORTED_MODES; + return 0; +} + +static int adp1653_get_mode_details(void *priv_data, unsigned long mode, +struct flash_mode_details *details_p) +{ + int err = 0; + memset(details_p,0,sizeof(struct flash_mode_details)); + + details_p->led_type = 2; + + /* Still LED settings*/ + details_p->nbFaultRegisters = 1; + if(mode & (FLASH_MODE_STILL_LED | FLASH_MODE_STILL_LED_EXTERNAL_STROBE)){ + details_p->max_intensity_uAmp = FLASH_MAX_INTENSITY; + details_p->min_intensity_uAmp = FLASH_MIN_INTENSITY; + details_p->max_strobe_duration_uSecs = FLASH_MAX_STROBE_DURATION; + details_p->feature_bitmap = INTENSITY_PROGRAMMABLE | DURATION_PROGRAMMABLE; + goto out; + } + /*Video LED settings*/ + if(mode & FLASH_MODE_VIDEO_LED){ + details_p->max_intensity_uAmp = TORCH_MAX_INTENSITY; + details_p->min_intensity_uAmp = TORCH_MIN_INTENSITY; + details_p->max_strobe_duration_uSecs = 0; + details_p->feature_bitmap = INTENSITY_PROGRAMMABLE; + goto out; + } + /*Privacy Indicator settings */ + if(mode & FLASH_MODE_INDICATOR){ + details_p->max_intensity_uAmp = ILED_MAX_INTENSITY; + details_p->min_intensity_uAmp = ILED_MIN_INTENSITY; + details_p->max_strobe_duration_uSecs = 0; + details_p->feature_bitmap = INTENSITY_PROGRAMMABLE; + goto out; + } + DEBUG_LOG("Mode %lx, not supported\n",mode); + err = EINVAL; +out: + return err; +} + +static int adp1653_enable_flash_mode(void *priv_data, + unsigned long mode, int enable) +{ + int err = 0; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + + if(enable){ + + if(!(mode & ADP1653_SUPPORTED_MODES)){ + DEBUG_LOG("Unsupported mode %lx\n",mode); + err = -EINVAL; + goto out; + } + /*Nothing to be done in enabling, just set current mode and return*/ + /*May be enable disable can be done here but why not enable in + *probe and keep it on always + */ + adp1653_trigger_strobe(priv_p,0); + priv_p->curr_mode = mode; + }else{ + adp1653_trigger_strobe(priv_p,0); + priv_p->curr_mode =0; + } +out: + return err; +} + +static int adp1653_configure_flash_mode(void *priv,unsigned long mode, +struct flash_mode_params *params_p) +{ + int err = 0; + unsigned char intensity_code; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv; + + if(!(mode & ADP1653_SUPPORTED_MODES)){ + DEBUG_LOG("Mode %lx not supported\n",mode); + err = -EINVAL; + goto out; + } + switch(mode){ + case FLASH_MODE_STILL_LED: + case FLASH_MODE_STILL_LED_EXTERNAL_STROBE: + { + FLASH_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp); + if(params_p->duration_uSecs){ + DURATION_USEC_TO_CODE(priv_p->flash_duration, + params_p->duration_uSecs); + DEBUG_LOG("Duration %lu, code 0x%x\n",params_p->duration_uSecs, + priv_p->flash_duration); + priv_p->flash_duration |= TIMER_ENABLE; + }else{ + priv_p->flash_duration = 0; + } + priv_p->flash_intensity = intensity_code << 3; + } + break; + case FLASH_MODE_VIDEO_LED: + { + TORCH_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp); + DEBUG_LOG("Torch mode setting intensity 0x%x, current(uA) %lu\n", + intensity_code,params_p->intensity_uAmp); + priv_p->torch_intensity = intensity_code << 3; + } + break; + case FLASH_MODE_INDICATOR: + { + ILED_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp); + DEBUG_LOG("ILED setting intensity 0x%x, current(uA) %lu\n", + intensity_code,params_p->intensity_uAmp); + priv_p->indicator_intensity = intensity_code; + } + break; + default: + err = -EINVAL; + DEBUG_LOG("Unsupported mode %lx\n",mode); + break; + } + + if((mode == FLASH_MODE_STILL_LED_EXTERNAL_STROBE) || (mode == FLASH_MODE_STILL_LED)) + { + adp1653_trigger_strobe(priv_p,0); + DEBUG_LOG("CONFIG_TIMER_REG : 0x%x\n",priv_p->flash_duration); + DEBUG_LOG("OUTPUT_SEL_REG : 0x%x\n",priv_p->flash_intensity); + + /*TimeOut Must be programmed before Intensity*/ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,CONFIG_TIMER_REG, + priv_p->flash_duration); + if(err){ + DEBUG_LOG("I2C: Unsable to write timer config, err %d\n",err); + goto out; + } + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG, + priv_p->flash_intensity); + if(err){ + DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG , err %d\n",err); + goto out; + } + } +out: + return err; +} + +static int adp1653_set_intensity(struct adp1653_priv_data *priv_p, uint8_t intensity) +{ + return i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,intensity); +} + +static int adp1653_strobe_still_led(struct adp1653_priv_data *priv_p,int enable) +{ + int err=0,gpio_val; + uint8_t intensity,duration; + + if(enable){ + intensity = priv_p->flash_intensity; + duration = priv_p->flash_duration; + gpio_val = 1; + }else{ + intensity = 0; + duration = 0; + gpio_val = 0; + } + + err = adp1653_set_intensity(priv_p,intensity); + if(err){ + DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG reg, err %d\n",err); + goto out; + } + + /*TimeOut Must be programmed before Intensity*/ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,CONFIG_TIMER_REG, + priv_p->flash_duration); + if(err){ + DEBUG_LOG("I2C: Unsable to write timer config, err %d\n",err); + goto out; + } + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,intensity); + if(err){ + DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG, err %d\n",err); + goto out; + } + + if (strobe_gpio != 0 ) + { + gpio_set_value(priv_p->strobe_gpio,gpio_val); + } + +out: + return err; +} + +static int adp1653_trigger_strobe(void *priv, int enable) +{ + int err = 0; + uint8_t intensity; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv; + + switch(priv_p->curr_mode){ + case FLASH_MODE_STILL_LED: + case FLASH_MODE_STILL_LED_EXTERNAL_STROBE: + err = adp1653_strobe_still_led(priv_p,enable); + break; + case FLASH_MODE_VIDEO_LED: + { + if(enable) + intensity = priv_p->torch_intensity; + else + intensity = 0; + err = adp1653_set_intensity(priv_p,intensity); + } + break; + case FLASH_MODE_INDICATOR: + { + if(enable) + intensity = priv_p->indicator_intensity; + else + intensity =0; + err = adp1653_set_intensity(priv_p,intensity); + } + break; + default: + DEBUG_LOG("Unsupported mode %lx\n",priv_p->curr_mode); + goto out; + } + if(err){ + DEBUG_LOG("Unable to enable/disable %d, strobe. Mode %lx, err %d\n",enable, + priv_p->curr_mode,err); + goto out; + } + disable_irq(priv_p->i2c_client->irq); + if(enable) + SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_LIT); + else + CLR_FLASH_STATUS(priv_p->status,FLASH_STATUS_LIT); + + enable_irq(priv_p->i2c_client->irq); + +out: + return err; +} +#define FLASH_ERR_ALL (FLASH_ERR_OVER_CHARGE |FLASH_ERR_OVER_HEAT | \ + FLASH_ERR_SHORT_CIRCUIT | FLASH_ERR_TIMEOUT | \ + FLASH_ERR_OVER_VOLTAGE) +int adp1653_get_status(void *priv_data,unsigned long *status) +{ + struct adp1653_priv_data *priv_p= (struct adp1653_priv_data *)priv_data; + disable_irq(priv_p->i2c_client->irq); + if(priv_p->fault){ + if(priv_p->fault & OVER_VOLTAGE_FAULT) + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_OVER_VOLTAGE); + if(priv_p->fault & TIMEOUT_FAULT) + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_TIMEOUT); + if(priv_p->fault & OVER_TEMPERATURE_FAULT) + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_OVER_HEAT); + if(priv_p->fault & SHORT_CIRCUIT_FAULT){ + CLR_FLASH_STATUS(priv_p->status,FLASH_STATUS_READY); + SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_BROKEN); + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_SHORT_CIRCUIT); + } + priv_p->fault =0; + }else{ + CLR_FLASH_ERROR(priv_p->status,FLASH_ERR_ALL); + } + enable_irq(priv_p->i2c_client->irq); + *status = priv_p->status; + return 0; +} + +int adp1653_get_selftest_modes(void *priv_data, unsigned long *modes) +{ + int err; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + err = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG); + if (err) *modes = 0x0; + else *modes = ADP1653_SELFTEST_SUPPORTED_MODES; + return 0; +} + +int adp1653_get_fault_registers(void *priv_data, unsigned long mode, unsigned long *status) +{ + int err = 0; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + + *status = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG); + + /* clear fault register */ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,0); + if(0 != err) + { + DEBUG_LOG("Unable to write OUTPUT_SEL_REG, err %d\n",err); + } + return err; +} + +struct flash_chip_ops adp1653_ops = { + .get_modes = adp1653_get_modes, + .get_mode_details = adp1653_get_mode_details, + .get_status = adp1653_get_status, + .enable_flash_mode = adp1653_enable_flash_mode, + .configure_flash_mode = adp1653_configure_flash_mode, + .trigger_strobe = adp1653_trigger_strobe, + .get_selftest_modes = adp1653_get_selftest_modes, + .get_fault_registers = adp1653_get_fault_registers +}; + +static irqreturn_t adp1653_irq_hdlr(int irq_no,void *data) +{ + int err; + struct adp1653_priv_data *priv_p= (struct adp1653_priv_data *)data; + + priv_p->fault = i2c_smbus_read_byte_data(priv_p->i2c_client, + FAULT_STATUS_REG); + DEBUG_LOG("Got Fault, status 0x%x\n",priv_p->fault); + /*Writing 0 to OUTPUT_SEL_REG clears the interrtup + *and FAULT_STATUS_REG register + */ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,0); + if(err) + DEBUG_LOG("Unable to write OUTPUT_SEL_REG to clr intr, err %d\n",err); + /*TBD: send even to user process*/ + return IRQ_HANDLED; +} +static int __devinit adp1653_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err=0; + struct flash_chip *flash_chip_p=NULL; + struct adp1653_priv_data *priv_p=NULL; + + DEBUG_LOG("> adp1653_probe\n"); + + priv_p = kzalloc(sizeof(struct adp1653_priv_data),GFP_KERNEL); + if(!priv_p){ + DEBUG_LOG("Kmalloc failed for priv data\n"); + err = ENOMEM; + goto out; + } + priv_p->i2c_client = client; + flash_chip_p = kzalloc(sizeof(struct flash_chip),GFP_KERNEL); + if(!flash_chip_p){ + DEBUG_LOG("Kmalloc failed for flash_chip_p"); + err = ENOMEM; + goto out; + } + flash_chip_p->priv_data = priv_p; + flash_chip_p->ops = &adp1653_ops; + SET_FLASHCHIP_TYPE(flash_chip_p,FLASH_TYPE_HPLED); + SET_FLASHCHIP_ID(flash_chip_p,ADP1653_ID); + + strncpy(flash_chip_p->name,"Adp1653",FLASH_NAME_SIZE); + + i2c_set_clientdata(client,priv_p); + /*Request GPIO and Register IRQ if supported by platform and flash chip*/ + + if(machine_is_hrefv60()) + enable_gpio = 21; + else + enable_gpio = 272; + + if(enable_gpio){ + err = gpio_request(enable_gpio,"Camera LED flash Enable"); + if(err){ + DEBUG_LOG("Unable to get GPIO %d, for enable\n",enable_gpio); + goto out; + } + priv_p->enable_gpio = enable_gpio; + + err = gpio_direction_output(priv_p->enable_gpio, 1); + if(err){ + DEBUG_LOG("Unable to set GPIO %lu in output mode, err %d\n",priv_p->enable_gpio,err); + gpio_free(priv_p->enable_gpio); + goto out; + } + gpio_set_value(priv_p->enable_gpio, 1); + } + + if (strobe_gpio != 0) + { + err = gpio_request(STR_GPIO,"Camera flash strobe\n"); + if(err){ + DEBUG_LOG("Unable to request strobe GPIO\n"); + // somebody else requested this gpio ... + //goto out; + } + + err = gpio_direction_output(STR_GPIO, 0); + if(err){ + DEBUG_LOG("Unable to set GPIO %d in output mode, err %d\n",STR_GPIO,err); + goto out; + } + } + else + { + err = gpio_request(STR_GPIO,"Camera flash strobe\n"); + if(err){ + DEBUG_LOG("Unable to request strobe GPIO\n"); + // somebody else requested this gpio ... + //goto out; + } + + err = gpio_direction_input(STR_GPIO); + if(err){ + DEBUG_LOG("Unable to set GPIO %d in input mode, err %d\n",STR_GPIO,err); + goto out; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + err = nmk_gpio_set_pull(STR_GPIO, NMK_GPIO_PULL_DOWN); +#endif + if(err){ + DEBUG_LOG("Unable to set pull down on GPIO %d\n",STR_GPIO); + goto out; + } + + } + + priv_p->strobe_gpio = STR_GPIO; + + if(client->irq){ + err = request_threaded_irq(client->irq,NULL,adp1653_irq_hdlr, + IRQF_ONESHOT|IRQF_TRIGGER_FALLING, + "Adp1653 flash",priv_p); + if(err){ + DEBUG_LOG("Unable to register flash IRQ handler, irq %d, err %d\n", + client->irq,err); + goto out; + } + } + err = register_flash_chip(flash_position,flash_chip_p); + if(err){ + DEBUG_LOG("Failed to register Adp1653 as flash for %s camera\n", + (flash_position?"Primary":"Secondary")); + goto out; + } + SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_READY); + DEBUG_LOG("< adp1653_probe ok\n"); + return err; +out: + if(priv_p->irq_no) + free_irq(priv_p->irq_no,NULL); + if(priv_p->enable_gpio) + gpio_free(priv_p->enable_gpio); + if(priv_p) + kfree(priv_p); + if(flash_chip_p) + kfree(flash_chip_p); + DEBUG_LOG("< adp1653_probe (%d)\n", err); + return err; +} + +static int __devexit adp1653_remove(struct i2c_client *client) +{ + int err=0; + /*Nothing here yet, implement it later.*/ + return err; +} +static const struct i2c_device_id adp1653_id[] = { + { "adp1653", 0}, + {} +}; +static struct i2c_driver adp1653_i2c_driver = { + .driver = { + .name = "adp1653", + .owner = THIS_MODULE, + }, + .probe = adp1653_probe, + .remove = __devexit_p(adp1653_remove), + .id_table = adp1653_id, +}; + +int adp1653_init(void){ + int err=0; + struct i2c_adapter *adap_p; + struct i2c_board_info info; + + /*Registration of I2C flash device is platform specific code + *Ideally it should be done from kernel (arch/arm/mach-XXX). + *Do it locally till the time it gets into platform code + *OR This portion (registration of device) and flash chip init + *Routine can be moved to Flash chip module init. + */ + DEBUG_LOG("getting I2C adaptor %d\n",adapter_i2c2); + adap_p = i2c_get_adapter(adapter_i2c2); + if(!adap_p){ + DEBUG_LOG("Unable to get I2C adaptor\n"); + goto out; + } + memset(&info,0,sizeof( struct i2c_board_info)); + + info.irq = __flash_gpio_to_irq(flash_irq); + + strcpy(&info.type[0],"adp1653"); + DEBUG_LOG("trying to register %s at position %d, GPIO enable %d, GPIO IRQ line %d\n", + info.type, + flash_position, + enable_gpio, + flash_irq); + + /* I2C framework expects least significant 7 bits as address, not complete + * 8 bits with bit 0 (read/write bit) + */ + info.addr = 0x60 >> 1; + + + err = i2c_add_driver(&adp1653_i2c_driver); + if(err) + { + DEBUG_LOG("Failed to register i2c driver\n"); + goto out; + } + + + DEBUG_LOG("Initialized adp1653\n"); + if(!i2c_new_device(adap_p,&info)){ + DEBUG_LOG("Unable to add i2c dev: %s (err=%d)\n",info.type, err); + goto out; + } + +out: + return err; +} + +/* +MODULE_DEPEND +*/ diff --git a/drivers/staging/camera_flash/adp1653.h b/drivers/staging/camera_flash/adp1653.h new file mode 100755 index 00000000000..3035ab56d99 --- /dev/null +++ b/drivers/staging/camera_flash/adp1653.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * License terms: GNU General Public License (GPL), version 2. + */ +#ifndef __ADP1653_H__ +#define __ADP1653_H__ + +#include <linux/types.h> +#define ADP1653_ID (0) /*chip does not give any id :) so be it zero!*/ + +#define OUTPUT_SEL_REG (0x00) +#define CONFIG_TIMER_REG (0x01) +#define SW_STROBE_REG (0x02) +#define FAULT_STATUS_REG (0x03) + +/* Fault codes, FALUT_STATUS_REG bits */ +#define OVER_VOLTAGE_FAULT (0x01) +#define TIMEOUT_FAULT (0x02) +#define OVER_TEMPERATURE_FAULT (0x04) +#define SHORT_CIRCUIT_FAULT (0x08) + +/*CONFIG_TIMER_REG bits*/ +#define TIMER_ENABLE (0x10) + +struct adp1653_priv_data{ + struct i2c_client *i2c_client; + unsigned long curr_mode; + unsigned long enable_gpio; + unsigned long strobe_gpio; + unsigned long irq_no; + unsigned long status; + uint8_t fault; + uint8_t flash_intensity; + uint8_t flash_duration; + uint8_t torch_intensity; + uint8_t indicator_intensity; +}; + +/*Intensity current limits in Micro Amps*/ +/* over 250mA flash current is reduced */ +/* do not know why, neither really care about */ +//#define FLASH_MAX_INTENSITY (500000) /*code - 31*/ +#define FLASH_MAX_INTENSITY (250000) +#define FLASH_MIN_INTENSITY (215000) /*code - 12*/ +#define TORCH_MAX_INTENSITY (200000) /*code - 11*/ +#define TORCH_MIN_INTENSITY (50000) /*code - 1*/ +#define ILED_MAX_INTENSITY (17500) /*Code - 7*/ +#define ILED_MIN_INTENSITY (2500) /*code - 1*/ + +#define FLASH_MAX_STROBE_DURATION (820000) /*820 uSec*/ + +#define DURATION_USEC_TO_CODE(_code,_duration) do{ \ + if(_duration > FLASH_MAX_STROBE_DURATION) \ + _duration = FLASH_MAX_STROBE_DURATION; \ + _code = (FLASH_MAX_STROBE_DURATION - _duration) / 54600;\ +}while(0); + +#define HPLED_UAMP_TO_CODE(_current) ((_current - 35000) / 15000) + +#define FLASH_UAMP_TO_CODE(_code,_current){ \ + if(_current > FLASH_MAX_INTENSITY) \ + _current = FLASH_MAX_INTENSITY; \ + if(_current < FLASH_MIN_INTENSITY) \ + _current = FLASH_MIN_INTENSITY; \ + _code = HPLED_UAMP_TO_CODE(_current); \ +}while(0) + +#define TORCH_UAMP_TO_CODE(_code,_current){ \ + if(_current > TORCH_MAX_INTENSITY) \ + _current = TORCH_MAX_INTENSITY; \ + if(_current < TORCH_MIN_INTENSITY) \ + _current = TORCH_MIN_INTENSITY; \ + _code = HPLED_UAMP_TO_CODE(_current); \ +}while(0) + +#define ILED_UAMP_TO_CODE(_code,_current) do { \ + if(_current > ILED_MAX_INTENSITY) \ + _current = ILED_MAX_INTENSITY; \ + _code = _current / ILED_MIN_INTENSITY; /* Min current: 2.5mA/2500uA*/ \ +}while(0) + +#endif diff --git a/drivers/staging/camera_flash/flash_common.c b/drivers/staging/camera_flash/flash_common.c new file mode 100644 index 00000000000..021ef016e39 --- /dev/null +++ b/drivers/staging/camera_flash/flash_common.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * camera flash: Flash driver to export camera flash to user space application. + * It supports two flashes, one for primary and one for secondary camera + * Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/kthread.h> +#include <linux/jiffies.h> +#include <linux/miscdevice.h> +#include <linux/camera_flash.h> +#include "flash_common.h" +#define DEBUG_LOG(...) printk(KERN_DEBUG "Camera Flash driver: " __VA_ARGS__) + +#define PRIMARY_CAMERA (0) +#define SECONDARY_CAMERA (1) +static struct miscdevice misc_dev; +struct flash_chip *flash_chips[2]; +struct fasync_struct * async_queue; +struct task_struct* ptaskStruct; +wait_queue_head_t waitQueue; +int waitCondition = 0; +struct flash_ioctl_args_t flashArg; + +#define COPY_ARG_FROM_USER(_to,_from_usr) do{ \ + memset((_to),0,sizeof(struct flash_ioctl_args_t)); \ + if (copy_from_user((_to), (struct flash_ioctl_args_t*) (_from_usr), sizeof(struct flash_ioctl_args_t))) { \ + DEBUG_LOG("Could not copy data from userspace successfully\n"); \ + break; \ + } \ +}while(0) + +#define COPY_ARG_TO_USER(_to_usr,_from) do{ \ + if (copy_to_user((struct flash_ioctl_args_t *)(_to_usr), (_from), sizeof(struct flash_ioctl_args_t))) { \ + DEBUG_LOG("Could not copy data from userspace successfully\n"); \ + break; \ + } \ +}while(0) + + +static long flash_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg) +{ + int err=0; + struct flash_chip *flash_p = NULL; + struct flash_chip_ops *ops = NULL; + char *my_name=NULL; + struct flash_ioctl_args_t flash_arg; + + if (_IOC_TYPE(cmd) != FLASH_MAGIC_NUMBER) { + printk(KERN_ALERT "Flash driver: Not an ioctl for this module\n"); + err = -EINVAL; + } + + COPY_ARG_FROM_USER(&flash_arg,arg); + + if(flash_arg.cam == SECONDARY_CAMERA || flash_arg.cam == PRIMARY_CAMERA) + flash_p = flash_chips[flash_arg.cam]; + else{ + DEBUG_LOG("unsupported cam %lu\n",flash_arg.cam); + err = -ENODEV; + goto out; + } + my_name = flash_arg.cam ?"Secondary":"Primary"; + + if (flash_arg.cam == PRIMARY_CAMERA) + { + ops = flash_p->ops; + } + + switch(cmd){ + case FLASH_GET_MODES: + { + if (flash_arg.cam == PRIMARY_CAMERA) + { + err = ops->get_modes(flash_p->priv_data,&flash_arg.flash_mode); + if(!err){ + DEBUG_LOG("Supported flash modes for %s camera: %lx\n", + my_name,flash_arg.flash_mode); + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("unable to get supported modes for %s camera\n",my_name); + } + } + else + { + flash_arg.flash_mode = FLASH_MODE_NONE; + COPY_ARG_TO_USER(arg,&flash_arg); + } + } + break; + case FLASH_GET_MODE_DETAILS: + { + err = ops->get_mode_details(flash_p->priv_data,flash_arg.flash_mode, + &flash_arg.mode_arg.details); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("Unable to get mode details for %s camera, flash mode %lx\n", + my_name,flash_arg.flash_mode); + } + } + break; + case FLASH_ENABLE_MODE: + case FLASH_DISABLE_MODE: + { + int enable=0; + if(cmd == FLASH_ENABLE_MODE){ + enable = 1; + } + err = ops->enable_flash_mode(flash_p->priv_data,flash_arg.flash_mode,enable); + if(err){ + DEBUG_LOG("Unable to %s: %s camera, flash mode %lx\n", + (enable ?"Enable":"Disable"), my_name,flash_arg.flash_mode); + } + } + break; + case FLASH_CONFIGURE_MODE: + err = ops->configure_flash_mode(flash_p->priv_data,flash_arg.flash_mode, + &flash_arg.mode_arg.params); + if(err){ + DEBUG_LOG("Unable to configure %s camera, flash mode %lx\n", + my_name,flash_arg.flash_mode); + } + break; + case FLASH_TRIGGER_STROBE: + err = ops->trigger_strobe(flash_p->priv_data,flash_arg.mode_arg.strobe_enable); + if(err){ + DEBUG_LOG("Unable to %s: %s camera strobe trigger, mode %lx\n", + (arg ?"Enable":"Disable"), my_name,flash_arg.flash_mode); + } + break; + case FLASH_GET_STATUS: + err = ops->get_status(flash_p->priv_data,&flash_arg.status); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("Unable to get status details for %s camera, flash mode %lx\n", + my_name,flash_arg.flash_mode); + } + break; + case FLASH_GET_LIFE_COUNTER: + DEBUG_LOG("Not Implemented\n"); + break; + case FLASH_SELF_TEST: + flashArg = flash_arg; + if (0 != (flashArg.cam & (FLASH_SELFTEST_FLASH | FLASH_SELFTEST_FLASH_WITH_STROBE))) + { + err = ENODEV; + } + else + { + /* wake up worker thread */ + waitCondition = 1; + wake_up_interruptible(&waitQueue); + } + break; + case FLASH_GET_SELF_TEST_MODES: + { + if (flash_arg.cam == PRIMARY_CAMERA) + { + err = ops->get_selftest_modes(flash_p->priv_data,&flash_arg.flash_mode); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("unable to get supported modes for %s camera\n",my_name); + } + } + else + { + flash_arg.flash_mode = FLASH_SELFTEST_NONE; + COPY_ARG_TO_USER(arg,&flash_arg); + } + break; + } + case FLASH_GET_FAULT_REGISTERS: + { + err = ops->get_fault_registers(flash_p->priv_data,flash_arg.flash_mode,&flash_arg.status); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("unable to get supported modes for %s camera\n",my_name); + } + + break; + } + case FLASH_GET_SELF_TEST_RESULT: + { + COPY_ARG_TO_USER(arg,&flashArg); + DEBUG_LOG("FLASH_GET_SELF_TEST_RESULT arg : 0x%lx\n", flashArg.status); + break; + } + default: + DEBUG_LOG("Unknown command %x\n",cmd); + + } +out: + return err; +} + +int worker_thread (void* data) +{ + int err = 0; + struct flash_chip *flash_p=NULL; + struct flash_chip_ops *ops=NULL; + struct flash_mode_params params; + struct flash_mode_details details; + + while (1) + { + /* waiting for some job to do */ + wait_event_interruptible(waitQueue, (waitCondition != 0)); + waitCondition = 0; + + DEBUG_LOG("worker_thread wakes up\n"); + /* do we need to stop ? */ + err = kthread_should_stop(); + if (0 != err) + { + DEBUG_LOG("worker_thread stops\n"); + break; + } + + /* do the job */ + flash_p = flash_chips[flashArg.cam]; + ops = flash_p->ops; + + /* clear fault registers */ + err = ops->get_fault_registers(flash_p->priv_data, FLASH_MODE_INDICATOR, &flashArg.status); + if (0 != err) + { + flashArg.status = flashArg.flash_mode; + flashArg.flash_mode = 0; + } + flashArg.status = 0; + + /* do all selftests */ + while (flashArg.flash_mode != FLASH_SELFTEST_NONE) + { + if (0 != (flashArg.flash_mode & FLASH_SELFTEST_CONNECTION)) + { + err = ops->get_mode_details(flash_p->priv_data, FLASH_MODE_INDICATOR, &details); + if (0 != err) + { + DEBUG_LOG("not able to get mode FLASH_MODE_INDICATOR details\n"); + flashArg.status |= FLASH_SELFTEST_CONNECTION; + } + flashArg.flash_mode &= ~FLASH_SELFTEST_CONNECTION; + } + else if (0 != (flashArg.flash_mode & (FLASH_SELFTEST_FLASH | FLASH_SELFTEST_FLASH_WITH_STROBE))) + { + if (0 != (flashArg.flash_mode & FLASH_SELFTEST_FLASH)) + { + flashArg.status |= FLASH_SELFTEST_FLASH; + flashArg.flash_mode &= ~FLASH_SELFTEST_FLASH; + } + else + { + flashArg.status |= FLASH_SELFTEST_FLASH_WITH_STROBE; + flashArg.flash_mode &= ~FLASH_SELFTEST_FLASH_WITH_STROBE; + } + } + /* FLASH_SELFTEST_VIDEO_LIGHT | FLASH_SELFTEST_AF_LIGHT | FLASH_SELFTEST_INDICATOR | FLASH_SELFTEST_TORCH_LIGHT */ + else + { + unsigned long currentSelftest = FLASH_SELFTEST_NONE; + unsigned long currentFlashMode = FLASH_MODE_NONE; + + if (0 != (flashArg.flash_mode & FLASH_SELFTEST_VIDEO_LIGHT)) + { + currentSelftest = FLASH_SELFTEST_VIDEO_LIGHT; + currentFlashMode = FLASH_MODE_VIDEO_LED; + } + else if (0 != (flashArg.flash_mode & FLASH_SELFTEST_AF_LIGHT)) + { + currentSelftest = FLASH_SELFTEST_AF_LIGHT; + currentFlashMode = FLASH_MODE_AF_ASSISTANT; + } + else if (0 != (flashArg.flash_mode & FLASH_SELFTEST_INDICATOR)) + { + currentSelftest = FLASH_SELFTEST_INDICATOR; + currentFlashMode = FLASH_MODE_INDICATOR; + } + else + { + currentSelftest = FLASH_SELFTEST_TORCH_LIGHT; + currentFlashMode = FLASH_MODE_VIDEO_LED; + } + + err = ops->get_mode_details(flash_p->priv_data, currentFlashMode, &details); + if (0 != err) + { + DEBUG_LOG("not able to get mode 0x%lx details\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + err = ops->enable_flash_mode(flash_p->priv_data, currentFlashMode, 1); + if (0 != err) + { + DEBUG_LOG("not able to enable flash mode 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + params.duration_uSecs = 0; + params.intensity_uAmp = details.max_intensity_uAmp; + params.timeout_uSecs = 0; + err = ops->configure_flash_mode(flash_p->priv_data, currentFlashMode, ¶ms); + if (0 != err) + { + DEBUG_LOG("not able to configure flash mode 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + err = ops->trigger_strobe(flash_p->priv_data,1); + if (0 != err) + { + DEBUG_LOG("not able to strobe, mode : 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + wait_event_timeout(waitQueue, 0, msecs_to_jiffies(1000)); + + err = ops->trigger_strobe(flash_p->priv_data,0); + if (0 != err) + { + DEBUG_LOG("not able to strobe, mode : 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + flashArg.flash_mode &= ~currentSelftest; + } + } + + /* job's done ! */ + flash_async_notify(); + } + return 0; +} + +int flash_open(struct inode *node, struct file *file_p) +{ + // init sleep queue + init_waitqueue_head(&waitQueue); + + // start worker thread + ptaskStruct = kthread_run (&worker_thread, NULL, "flashDriverWorker"); + + return 0; +} + +int register_flash_chip(unsigned int cam, struct flash_chip *flash_p) +{ + int err =0; + DEBUG_LOG("Registering cam %d\n", cam); + DEBUG_LOG("flash_p: name=%s\n", flash_p->name); + if(cam > 1 || !flash_p){ + DEBUG_LOG("Registration: something is wrong! cam %d, flash_p %x \n",cam,(int)flash_p); + err = EINVAL; + goto out; + } + if(!flash_chips[cam]){ + flash_chips[cam] = flash_p; + DEBUG_LOG("Registered flash: id %lx, %s for camera %d\n", + flash_p->id,flash_p->name,cam); + }else{ + DEBUG_LOG("%s flash already registered for camera %d, ignore flash %s\n", + flash_chips[cam]->name,cam, flash_p->name); + } +out: + return err; +} + +int flash_async_notify () +{ + kill_fasync(&async_queue, SIGIO, POLL_IN); + return 0; +} + +static int flash_fasync(int fd, struct file *filp, int mode) +{ + DEBUG_LOG("registered async notification on %d fd\n",fd); + return fasync_helper(fd, filp, mode, &async_queue); +} + +static int flash_release(struct inode *node, struct file *file_p) +{ + int err = 0; + + fasync_helper(-1, file_p, 0, &async_queue); + + // stop worker thread + waitCondition = 1; + err = kthread_stop(ptaskStruct); + return err; +} + +static struct file_operations flash_fops = { + owner:THIS_MODULE, + unlocked_ioctl:flash_ioctl, + open:flash_open, + release:flash_release, + fasync:flash_fasync, +}; + +int major_device_number; + +/*Temporary here (adp_init)*/ +extern int adp1653_init(void); +static int __init flash_init(void) +{ + int err = 0; + err = adp1653_init(); + if(err){ + DEBUG_LOG("Unable to initialize adp1653, err %d\n",err); + goto out; + } + /* Register misc device */ + misc_dev.minor = MISC_DYNAMIC_MINOR; + misc_dev.name = "camera_flash"; + misc_dev.fops = &flash_fops; + err = misc_register(&misc_dev); + if (err < 0) { + printk(KERN_INFO "camera_flash driver misc_register failed (%d)\n", err); + return err; + } else { + major_device_number = err; + printk(KERN_INFO "camera_flash driver initialized with minor=%d\n", misc_dev.minor); + } +out: + return err; +} + +static void __exit flash_exit(void) +{ + misc_deregister(&misc_dev); + printk(KERN_INFO"Camera flash driver unregistered\n"); +} + +module_init(flash_init); +module_exit(flash_exit); +MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(register_flash_chip); +EXPORT_SYMBOL(flash_async_notify); diff --git a/drivers/staging/camera_flash/flash_common.h b/drivers/staging/camera_flash/flash_common.h new file mode 100755 index 00000000000..a73c2279d19 --- /dev/null +++ b/drivers/staging/camera_flash/flash_common.h @@ -0,0 +1,56 @@ +#ifndef __FLASH_COMMON_H__ +#define __FLASH_COMMON_H__ + +#include <linux/camera_flash_bitfields.h> + +struct flash_chip_ops{ + int (*get_modes)( void *priv_data, unsigned long *modes); + int (*get_mode_details)(void *priv_data,unsigned long mode, + struct flash_mode_details *details_p); + int (*enable_flash_mode) (void *priv_data,unsigned long mode, + int enable); + int (*configure_flash_mode) (void *priv_data, unsigned long mode, + struct flash_mode_params *params_p); + int (*trigger_strobe) (void *priv_data, int enable); + int (*get_life_counter) (void *priv_data); + int (*get_status) (void *priv_data, unsigned long *status); + int (*get_selftest_modes) (void *priv_data, + unsigned long *modes); + int (*get_fault_registers) (void *priv_data, unsigned long mode, + unsigned long *status); +}; + +#define FLASH_TYPE_XENON (0x1) +#define FLASH_TYPE_HPLED (0x2) + +#define SET_FLASHCHIP_TYPE(flash_chip_p,_TYPE) ((flash_chip_p)->id |= _TYPE) +#define GET_FLASHHIP_TYPE(flash_chip_p) ((flash_chip_p)->id & 0xffff) +#define GET_FLASHCHIP_ID(flash_chip_p) ((flash_chip_p)->id >> 16) +#define SET_FLASHCHIP_ID(flash_chip_p,_ID) ((flash_chip_p)->id |= (_ID << 16)) + +struct flash_chip { + unsigned long id; + struct flash_chip_ops *ops; + void *priv_data; + unsigned char name[FLASH_NAME_SIZE]; +}; + +/** + * struct flash_platform_data: + * platform specific data For flash chip driver + * @cam : 0 - primary, 1 - secondary + * @strobe_gpio: GPIO used as strobe + * @enable_gpio: GPIO used for enable/reset input + */ +struct flash_platform_data{ + unsigned long cam; + unsigned long strobe_gpio; + unsigned long strobe_gpio_alt_func; + unsigned long enable_gpio; + unsigned long enable_gpio_alt_func; +}; + +extern int register_flash_chip(unsigned int cam, struct flash_chip *flash_p); +extern int flash_async_notify (void ); + +#endif |