Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 9c222f53fe
Fetching contributors…

Cannot retrieve contributors at this time

715 lines (631 sloc) 17.314 kB
/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/leds-pmic8058.h>
#include <linux/pwm.h>
#include <linux/pmic8058-pwm.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <mach/pmic.h>
#include <mach/camera.h>
#include <mach/gpio.h>
/*BU5D07741 descpription: camera flash light*/
#ifdef CONFIG_HUAWEI_KERNEL
#include <linux/mfd/pmic8058.h>
#include <linux/gpio.h>
#ifdef CONFIG_HUAWEI_EVALUATE_POWER_CONSUMPTION
#include <mach/msm_battery.h>
#define CAMERA_FLASH_CUR_DIV 10
#endif
struct i2c_client *sx150x_client;
#define CAMERA_LED_TORCH_MA 50
#define CAMERA_LED_TORCH_LOW_MA 50
#define CAMERA_LED_TORCH_MIDDLE_MA 100
#define CAMERA_LED_TORCH_HIGH_MA 150
#define PM8058_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + NR_GPIO_IRQS)*/
static struct pm_gpio camera_flash = {
.direction = PM_GPIO_DIR_OUT,
.output_buffer = PM_GPIO_OUT_BUF_CMOS,
.output_value = 0,
.pull = PM_GPIO_PULL_NO,
.vin_sel = 0,
.out_strength = PM_GPIO_STRENGTH_HIGH,
.function = PM_GPIO_FUNC_2,
.inv_int_pol = 1,
};
#endif
struct timer_list timer_flash;
static struct msm_camera_sensor_info *sensor_data;
enum msm_cam_flash_stat{
MSM_CAM_FLASH_OFF,
MSM_CAM_FLASH_ON,
};
#if defined CONFIG_HUAWEI_FEATURE_TPS61310
static struct i2c_client *tps61310_client;
#endif
#if defined CONFIG_MSM_CAMERA_FLASH_SC628A
static struct i2c_client *sc628a_client;
static const struct i2c_device_id sc628a_i2c_id[] = {
{"sc628a", 0},
{ }
};
static int32_t sc628a_i2c_txdata(unsigned short saddr,
unsigned char *txdata, int length)
{
struct i2c_msg msg[] = {
{
.addr = saddr,
.flags = 0,
.len = length,
.buf = txdata,
},
};
if (i2c_transfer(sc628a_client->adapter, msg, 1) < 0) {
CDBG("sc628a_i2c_txdata faild 0x%x\n", saddr);
return -EIO;
}
return 0;
}
static int32_t sc628a_i2c_write_b_flash(uint8_t waddr, uint8_t bdata)
{
int32_t rc = -EFAULT;
unsigned char buf[2];
if (!sc628a_client)
return -ENOTSUPP;
memset(buf, 0, sizeof(buf));
buf[0] = waddr;
buf[1] = bdata;
rc = sc628a_i2c_txdata(sc628a_client->addr>>1, buf, 2);
if (rc < 0) {
CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
waddr, bdata);
}
usleep_range(4000, 5000);
return rc;
}
static int sc628a_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc = 0;
CDBG("sc628a_probe called!\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pr_err("i2c_check_functionality failed\n");
goto probe_failure;
}
sc628a_client = client;
CDBG("sc628a_probe successed! rc = %d\n", rc);
return 0;
probe_failure:
pr_err("sc628a_probe failed! rc = %d\n", rc);
return rc;
}
static struct i2c_driver sc628a_i2c_driver = {
.id_table = sc628a_i2c_id,
.probe = sc628a_i2c_probe,
.remove = __exit_p(sc628a_i2c_remove),
.driver = {
.name = "sc628a",
},
};
#endif
static int config_flash_gpio_table(enum msm_cam_flash_stat stat,
struct msm_camera_sensor_strobe_flash_data *sfdata)
{
int rc = 0, i = 0;
int msm_cam_flash_gpio_tbl[][2] = {
{sfdata->flash_trigger, 1},
{sfdata->flash_charge, 1},
{sfdata->flash_charge_done, 0}
};
if (stat == MSM_CAM_FLASH_ON) {
for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
rc = gpio_request(msm_cam_flash_gpio_tbl[i][0],
"CAM_FLASH_GPIO");
if (unlikely(rc < 0)) {
pr_err("%s not able to get gpio\n", __func__);
for (i--; i >= 0; i--)
gpio_free(msm_cam_flash_gpio_tbl[i][0]);
break;
}
if (msm_cam_flash_gpio_tbl[i][1])
gpio_direction_output(
msm_cam_flash_gpio_tbl[i][0], 0);
else
gpio_direction_input(
msm_cam_flash_gpio_tbl[i][0]);
}
} else {
for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
gpio_direction_input(msm_cam_flash_gpio_tbl[i][0]);
gpio_free(msm_cam_flash_gpio_tbl[i][0]);
}
}
return rc;
}
int msm_camera_flash_current_driver(
struct msm_camera_sensor_flash_current_driver *current_driver,
unsigned led_state)
{
int rc = 0;
#if defined CONFIG_LEDS_PMIC8058
int idx;
const struct pmic8058_leds_platform_data *driver_channel =
current_driver->driver_channel;
int num_leds = driver_channel->num_leds;
CDBG("%s: led_state = %d\n", __func__, led_state);
/* Evenly distribute current across all channels */
switch (led_state) {
case MSM_CAMERA_LED_OFF:
for (idx = 0; idx < num_leds; ++idx) {
rc = pm8058_set_led_current(
driver_channel->leds[idx].id, 0);
if (rc < 0)
pr_err(
"%s: FAIL name = %s, rc = %d\n",
__func__,
driver_channel->leds[idx].name,
rc);
}
break;
case MSM_CAMERA_LED_LOW:
for (idx = 0; idx < num_leds; ++idx) {
rc = pm8058_set_led_current(
driver_channel->leds[idx].id,
current_driver->low_current/num_leds);
if (rc < 0)
pr_err(
"%s: FAIL name = %s, rc = %d\n",
__func__,
driver_channel->leds[idx].name,
rc);
}
break;
case MSM_CAMERA_LED_HIGH:
for (idx = 0; idx < num_leds; ++idx) {
rc = pm8058_set_led_current(
driver_channel->leds[idx].id,
current_driver->high_current/num_leds);
if (rc < 0)
pr_err(
"%s: FAIL name = %s, rc = %d\n",
__func__,
driver_channel->leds[idx].name,
rc);
}
break;
case MSM_CAMERA_LED_INIT:
case MSM_CAMERA_LED_RELEASE:
break;
default:
rc = -EFAULT;
break;
}
CDBG("msm_camera_flash_led_pmic8058: return %d\n", rc);
#endif /* CONFIG_LEDS_PMIC8058 */
return rc;
}
int msm_camera_flash_external(
struct msm_camera_sensor_flash_external *external,
unsigned led_state)
{
int rc = 0;
#if defined CONFIG_MSM_CAMERA_FLASH_SC628A
switch (led_state) {
case MSM_CAMERA_LED_INIT:
if (!sc628a_client) {
rc = i2c_add_driver(&sc628a_i2c_driver);
if (rc < 0 || sc628a_client == NULL) {
rc = -ENOTSUPP;
CDBG("I2C add driver failed");
return rc;
}
}
#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
if (external->expander_info && !sx150x_client) {
struct i2c_adapter *adapter =
i2c_get_adapter(external->expander_info->bus_id);
if (adapter)
sx150x_client = i2c_new_device(adapter,
external->expander_info->board_info);
if (!sx150x_client || !adapter) {
rc = -ENOTSUPP;
i2c_del_driver(&sc628a_i2c_driver);
sc628a_client = NULL;
return rc;
}
}
#endif
rc = gpio_request(external->led_en, "sc628a");
if (!rc) {
gpio_direction_output(external->led_en, 1);
} else {
goto err1;
}
rc = gpio_request(external->led_flash_en, "sc628a");
if (!rc) {
gpio_direction_output(external->led_flash_en, 1);
break;
}
gpio_set_value_cansleep(external->led_en, 0);
gpio_free(external->led_en);
err1:
i2c_del_driver(&sc628a_i2c_driver);
sc628a_client = NULL;
break;
case MSM_CAMERA_LED_RELEASE:
if (sc628a_client) {
gpio_set_value_cansleep(external->led_en, 0);
gpio_free(external->led_en);
gpio_set_value_cansleep(external->led_flash_en, 0);
gpio_free(external->led_flash_en);
}
#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
if (external->expander_info && sx150x_client) {
i2c_unregister_device(sx150x_client);
sx150x_client = NULL;
}
#endif
break;
case MSM_CAMERA_LED_OFF:
rc = sc628a_i2c_write_b_flash(0x02, 0x0);
if (sc628a_client) {
gpio_set_value_cansleep(external->led_en, 0);
gpio_set_value_cansleep(external->led_flash_en, 0);
}
break;
case MSM_CAMERA_LED_LOW:
if (sc628a_client) {
gpio_set_value_cansleep(external->led_en, 1);
gpio_set_value_cansleep(external->led_flash_en, 1);
usleep_range(2000, 3000);
}
rc = sc628a_i2c_write_b_flash(0x02, 0x06);
break;
case MSM_CAMERA_LED_HIGH:
if (sc628a_client) {
gpio_set_value_cansleep(external->led_en, 1);
gpio_set_value_cansleep(external->led_flash_en, 1);
usleep_range(2000, 3000);
}
rc = sc628a_i2c_write_b_flash(0x02, 0x49);
break;
default:
rc = -EFAULT;
break;
}
#endif
return rc;
}
static int msm_camera_flash_pwm(
struct msm_camera_sensor_flash_pwm *pwm,
unsigned led_state)
{
int rc = 0;
int PWM_PERIOD = USEC_PER_SEC / pwm->freq;
/*description:pwm camera flash*/
#ifdef CONFIG_HUAWEI_KERNEL
static struct pwm_device *flash_pwm = NULL;
#else
static struct pwm_device *flash_pwm;
#endif
/*If it is the first time to enter the function*/
if (!flash_pwm) {
#ifdef CONFIG_HUAWEI_KERNEL
rc = pm8xxx_gpio_config( 205, &camera_flash);
if (rc) {
pr_err("%s PMIC GPIO 24 write failed\n", __func__);
return rc;
}
#endif
flash_pwm = pwm_request(pwm->channel, "camera-flash");
if (flash_pwm == NULL || IS_ERR(flash_pwm)) {
pr_err("%s: FAIL pwm_request(): flash_pwm=%p\n",
__func__, flash_pwm);
flash_pwm = NULL;
return -ENXIO;
}
}
switch (led_state) {
case MSM_CAMERA_LED_LOW:
rc = pwm_config(flash_pwm,
(PWM_PERIOD/pwm->max_load)*pwm->low_load,
PWM_PERIOD);
if (rc >= 0)
rc = pwm_enable(flash_pwm);
break;
case MSM_CAMERA_LED_HIGH:
rc = pwm_config(flash_pwm,
(PWM_PERIOD/pwm->max_load)*pwm->high_load,
PWM_PERIOD);
if (rc >= 0)
rc = pwm_enable(flash_pwm);
break;
case MSM_CAMERA_LED_TORCH:
rc = pwm_config(flash_pwm,
(PWM_PERIOD/pwm->max_load)*CAMERA_LED_TORCH_MA,
PWM_PERIOD);
if (rc >= 0)
rc = pwm_enable(flash_pwm);
break;
case MSM_CAMERA_LED_TORCH_LOW:
rc = pwm_config(flash_pwm,
(PWM_PERIOD/pwm->max_load)*CAMERA_LED_TORCH_LOW_MA,
PWM_PERIOD);
if (rc >= 0)
rc = pwm_enable(flash_pwm);
break;
case MSM_CAMERA_LED_TORCH_MIDDLE:
rc = pwm_config(flash_pwm,
(PWM_PERIOD/pwm->max_load)*CAMERA_LED_TORCH_MIDDLE_MA,
PWM_PERIOD);
if (rc >= 0)
rc = pwm_enable(flash_pwm);
break;
case MSM_CAMERA_LED_TORCH_HIGH:
rc = pwm_config(flash_pwm,
(PWM_PERIOD/pwm->max_load)*CAMERA_LED_TORCH_HIGH_MA,
PWM_PERIOD);
if (rc >= 0)
rc = pwm_enable(flash_pwm);
break;
case MSM_CAMERA_LED_OFF:
pwm_disable(flash_pwm);
break;
case MSM_CAMERA_LED_INIT:
case MSM_CAMERA_LED_RELEASE:
break;
default:
rc = -EFAULT;
break;
}
return rc;
}
int msm_camera_flash_pmic(
struct msm_camera_sensor_flash_pmic *pmic,
unsigned led_state)
{
int rc = 0;
switch (led_state) {
case MSM_CAMERA_LED_OFF:
rc = pmic->pmic_set_current(pmic->led_src_1, 0);
if (pmic->num_of_src > 1)
rc = pmic->pmic_set_current(pmic->led_src_2, 0);
break;
case MSM_CAMERA_LED_LOW:
rc = pmic->pmic_set_current(pmic->led_src_1,
pmic->low_current);
if (pmic->num_of_src > 1)
rc = pmic->pmic_set_current(pmic->led_src_2, 0);
break;
case MSM_CAMERA_LED_HIGH:
rc = pmic->pmic_set_current(pmic->led_src_1,
pmic->high_current);
if (pmic->num_of_src > 1)
rc = pmic->pmic_set_current(pmic->led_src_2,
pmic->high_current);
break;
case MSM_CAMERA_LED_INIT:
case MSM_CAMERA_LED_RELEASE:
break;
default:
rc = -EFAULT;
break;
}
CDBG("flash_set_led_state: return %d\n", rc);
return rc;
}
int32_t msm_camera_flash_set_led_state(
struct msm_camera_sensor_flash_data *fdata, unsigned led_state)
{
int32_t rc;
if (fdata->flash_type != MSM_CAMERA_FLASH_LED ||
fdata->flash_src == NULL)
return -ENODEV;
switch (fdata->flash_src->flash_sr_type) {
case MSM_CAMERA_FLASH_SRC_PMIC:
rc = msm_camera_flash_pmic(&fdata->flash_src->_fsrc.pmic_src,
led_state);
break;
case MSM_CAMERA_FLASH_SRC_PWM:
rc = msm_camera_flash_pwm(&fdata->flash_src->_fsrc.pwm_src,
led_state);
break;
case MSM_CAMERA_FLASH_SRC_CURRENT_DRIVER:
rc = msm_camera_flash_current_driver(
&fdata->flash_src->_fsrc.current_driver_src,
led_state);
break;
case MSM_CAMERA_FLASH_SRC_EXT:
rc = msm_camera_flash_external(
&fdata->flash_src->_fsrc.ext_driver_src,
led_state);
break;
default:
rc = -ENODEV;
break;
}
#ifdef CONFIG_HUAWEI_EVALUATE_POWER_CONSUMPTION
/* start calculate flash consume */
switch (led_state) {
case MSM_CAMERA_LED_OFF:
huawei_rpc_current_consuem_notify(EVENT_CAMERA_FLASH_STATE, 0);
break;
case MSM_CAMERA_LED_LOW:
/* the consume depend on low_current */
huawei_rpc_current_consuem_notify(EVENT_CAMERA_FLASH_STATE, fdata->flash_src->_fsrc.pmic_src.low_current);
break;
case MSM_CAMERA_LED_HIGH:
/* the consume depend on high_current */
huawei_rpc_current_consuem_notify(EVENT_CAMERA_FLASH_STATE, fdata->flash_src->_fsrc.pmic_src.high_current);
break;
default:
break;
}
#endif
return rc;
}
static int msm_strobe_flash_xenon_charge(int32_t flash_charge,
int32_t charge_enable, uint32_t flash_recharge_duration)
{
gpio_set_value_cansleep(flash_charge, charge_enable);
if (charge_enable) {
timer_flash.expires = jiffies +
msecs_to_jiffies(flash_recharge_duration);
/* add timer for the recharge */
if (!timer_pending(&timer_flash))
add_timer(&timer_flash);
} else
del_timer_sync(&timer_flash);
return 0;
}
static void strobe_flash_xenon_recharge_handler(unsigned long data)
{
unsigned long flags;
struct msm_camera_sensor_strobe_flash_data *sfdata =
(struct msm_camera_sensor_strobe_flash_data *)data;
spin_lock_irqsave(&sfdata->timer_lock, flags);
msm_strobe_flash_xenon_charge(sfdata->flash_charge, 1,
sfdata->flash_recharge_duration);
spin_unlock_irqrestore(&sfdata->timer_lock, flags);
return;
}
static irqreturn_t strobe_flash_charge_ready_irq(int irq_num, void *data)
{
struct msm_camera_sensor_strobe_flash_data *sfdata =
(struct msm_camera_sensor_strobe_flash_data *)data;
/* put the charge signal to low */
gpio_set_value_cansleep(sfdata->flash_charge, 0);
return IRQ_HANDLED;
}
static int msm_strobe_flash_xenon_init(
struct msm_camera_sensor_strobe_flash_data *sfdata)
{
unsigned long flags;
int rc = 0;
spin_lock_irqsave(&sfdata->spin_lock, flags);
if (!sfdata->state) {
rc = config_flash_gpio_table(MSM_CAM_FLASH_ON, sfdata);
if (rc < 0) {
pr_err("%s: gpio_request failed\n", __func__);
goto go_out;
}
rc = request_irq(sfdata->irq, strobe_flash_charge_ready_irq,
IRQF_TRIGGER_RISING, "charge_ready", sfdata);
if (rc < 0) {
pr_err("%s: request_irq failed %d\n", __func__, rc);
goto go_out;
}
spin_lock_init(&sfdata->timer_lock);
/* setup timer */
init_timer(&timer_flash);
timer_flash.function = strobe_flash_xenon_recharge_handler;
timer_flash.data = (unsigned long)sfdata;
}
sfdata->state++;
go_out:
spin_unlock_irqrestore(&sfdata->spin_lock, flags);
return rc;
}
static int msm_strobe_flash_xenon_release
(struct msm_camera_sensor_strobe_flash_data *sfdata, int32_t final_release)
{
unsigned long flags;
spin_lock_irqsave(&sfdata->spin_lock, flags);
if (sfdata->state > 0) {
if (final_release)
sfdata->state = 0;
else
sfdata->state--;
if (!sfdata->state) {
free_irq(sfdata->irq, sfdata);
config_flash_gpio_table(MSM_CAM_FLASH_OFF, sfdata);
if (timer_pending(&timer_flash))
del_timer_sync(&timer_flash);
}
}
spin_unlock_irqrestore(&sfdata->spin_lock, flags);
return 0;
}
static void msm_strobe_flash_xenon_fn_init
(struct msm_strobe_flash_ctrl *strobe_flash_ptr)
{
strobe_flash_ptr->strobe_flash_init =
msm_strobe_flash_xenon_init;
strobe_flash_ptr->strobe_flash_charge =
msm_strobe_flash_xenon_charge;
strobe_flash_ptr->strobe_flash_release =
msm_strobe_flash_xenon_release;
}
int msm_strobe_flash_init(struct msm_sync *sync, uint32_t sftype)
{
int rc = 0;
switch (sftype) {
case MSM_CAMERA_STROBE_FLASH_XENON:
if (sync->sdata->strobe_flash_data) {
msm_strobe_flash_xenon_fn_init(&sync->sfctrl);
rc = sync->sfctrl.strobe_flash_init(
sync->sdata->strobe_flash_data);
} else
return -ENODEV;
break;
default:
rc = -ENODEV;
}
return rc;
}
int msm_strobe_flash_ctrl(struct msm_camera_sensor_strobe_flash_data *sfdata,
struct strobe_flash_ctrl_data *strobe_ctrl)
{
int rc = 0;
switch (strobe_ctrl->type) {
case STROBE_FLASH_CTRL_INIT:
if (!sfdata)
return -ENODEV;
rc = msm_strobe_flash_xenon_init(sfdata);
break;
case STROBE_FLASH_CTRL_CHARGE:
rc = msm_strobe_flash_xenon_charge(sfdata->flash_charge,
strobe_ctrl->charge_en,
sfdata->flash_recharge_duration);
break;
case STROBE_FLASH_CTRL_RELEASE:
if (sfdata)
rc = msm_strobe_flash_xenon_release(sfdata, 0);
break;
default:
pr_err("Invalid Strobe Flash State\n");
rc = -EINVAL;
}
return rc;
}
int msm_flash_ctrl(struct msm_camera_sensor_info *sdata,
struct flash_ctrl_data *flash_info)
{
int rc = 0;
sensor_data = sdata;
switch (flash_info->flashtype) {
case LED_FLASH:
rc = msm_camera_flash_set_led_state(sdata->flash_data,
flash_info->ctrl_data.led_state);
break;
case STROBE_FLASH:
rc = msm_strobe_flash_ctrl(sdata->strobe_flash_data,
&(flash_info->ctrl_data.strobe_ctrl));
break;
default:
pr_err("Invalid Flash MODE\n");
rc = -EINVAL;
}
return rc;
}
Jump to Line
Something went wrong with that request. Please try again.