/* * leds-msm-pmic.c - MSM PMIC LEDs driver. * * Copyright (c) 2009, 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <mach/pmic.h> #include <asm/mach-types.h> #ifdef CONFIG_HUAWEI_APPS #define MAX_KEYPAD_BL_LEVEL 64 #else #define MAX_KEYPAD_BL_LEVEL 16 #endif extern atomic_t kp_suspend_flag; atomic_t button_flag = ATOMIC_INIT(0); /* "0" means turn 0ff */ static void msm_keypad_bl_led_set(struct led_classdev *led_cdev, enum led_brightness value) { int ret = 0; /* U8110 has no keypad backlight */ if( machine_is_msm7x25_u8110() ) { /* there is nothing to do */ } /* U8100 series uses LCD_DRV to control keypad backlight */ else if( machine_is_msm7x25_u8100() || machine_is_msm7x25_u8105() \ || machine_is_msm7x25_u8107() || machine_is_msm7x25_u8109() ) { /*set keypad backlight current to 10mA*/ ret = pmic_set_led_intensity(LED_LCD, value / LED_FULL); } else if ( machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150() || machine_is_msm7x25_u8159() ) { ret = pmic_set_led_intensity(LED_KEYPAD, 0); /* never turn on */ if ( !atomic_read(&kp_suspend_flag) ) { ret = pmic_set_led_intensity(LED_LCD, value / LED_FULL); } if (LED_FULL == value) { atomic_set(&button_flag, 1); } else { atomic_set(&button_flag, 0); } } /* other series uses KEYPAD_DRV to control keypad backlight */ else { /*set keypad backlight current to 10mA*/ ret = pmic_set_led_intensity(LED_KEYPAD, value / LED_FULL); } if (ret) dev_err(led_cdev->dev, "can't set keypad backlight\n"); }
static int aps_12d_probe( struct i2c_client *client, const struct i2c_device_id *id) { int ret; struct aps_data *aps; int i; printk(KERN_INFO "aps_12d_probe enter\n "); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { printk(KERN_ERR "aps_12d_probe: need I2C_FUNC_I2C\n"); ret = -ENODEV; goto err_check_functionality_failed; } if(machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150()) { if((get_hw_sub_board_id() == HW_VER_SUB_VA) || ((get_hw_sub_board_id() == HW_VER_SUB_VB))) { printk(KERN_ERR "aps_12d_probe: aps is not supported in c8150 and u8150 T1 board!\n"); ret = -ENODEV; goto err_check_functionality_failed; } } aps = kzalloc(sizeof(*aps), GFP_KERNEL); if (aps == NULL) { ret = -ENOMEM; goto err_alloc_data_failed; } mutex_init(&aps->mlock); INIT_WORK(&aps->work, aps_12d_work_func); aps->client = client; i2c_set_clientdata(client, aps); printk(KERN_INFO "aps_12d_probe send command 2\n "); /* Command 2 register: 25mA,DC,12bit,Range1 */ ret = aps_i2c_reg_write(aps, APS_12D_REG_CMD2, \ (uint8_t)(APS_12D_IRDR_SEL_50MA << 6 | \ APS_12D_FREQ_SEL_DC << 4 | \ APS_12D_RES_SEL_12 << 2 | \ APS_12D_RANGE_SEL_ALS_1000)); if (ret < 0) { goto err_detect_failed; } if( machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150() || machine_is_msm7x25_u8159()\ || machine_is_msm7x25_u8160() || machine_is_msm7x25_u8130() || machine_is_msm7x25_c8510()) { range_index = 0; high_threshold = high_threshold_value[range_index]; low_threshold = low_threshold_value[range_index]; for(i = 0; i < TOTAL_RANGE_NUM; i++) { /* NOTE: do NOT use the last one */ up_range_value[i] = MAX_ADC_OUTPUT - high_threshold_value[i] - UP_RANGE_FIX; #ifdef DEBUG_AUTO_RANGE_ADJUST printk("up_range_value[%d] = %d.\n",i, up_range_value[i]); #endif } down_range_value[0] = 0; for(i = 1; i < TOTAL_RANGE_NUM; i++) { /* NOTE: do not use the first one */ down_range_value[i] = (MAX_ADC_OUTPUT - high_threshold_value[i-1] - (MAX_ADC_OUTPUT / ADJUST_GATE)) / 4; #ifdef DEBUG_AUTO_RANGE_ADJUST printk("down_range_value[%d] = %d\n",i, down_range_value[i]); #endif } } else if( machine_is_msm7x25_u8500() || machine_is_msm7x25_um840()) { high_threshold = 300; low_threshold = 280; } else if( machine_is_msm7x25_u8300() ) { /* set shutter value for u8300 */ high_threshold = 710; low_threshold = 650; } else { high_threshold = 780; low_threshold = 730; } if (sensor_dev == NULL) { aps->input_dev = input_allocate_device(); if (aps->input_dev == NULL) { ret = -ENOMEM; printk(KERN_ERR "aps_12d_probe: Failed to allocate input device\n"); goto err_input_dev_alloc_failed; } aps->input_dev->name = "sensors"; aps->input_dev->id.bustype = BUS_I2C; input_set_drvdata(aps->input_dev, aps); ret = input_register_device(aps->input_dev); if (ret) { printk(KERN_ERR "aps_probe: Unable to register %s input device\n", aps->input_dev->name); goto err_input_register_device_failed; } sensor_dev = aps->input_dev; } else { aps->input_dev = sensor_dev; } set_bit(EV_ABS, aps->input_dev->evbit); input_set_abs_params(aps->input_dev, ABS_LIGHT, 0, 10240, 0, 0); input_set_abs_params(aps->input_dev, ABS_DISTANCE, 0, 1, 0, 0); ret = misc_register(&light_device); if (ret) { printk(KERN_ERR "aps_12d_probe: light_device register failed\n"); goto err_light_misc_device_register_failed; } ret = misc_register(&proximity_device); if (ret) { printk(KERN_ERR "aps_12d_probe: proximity_device register failed\n"); goto err_proximity_misc_device_register_failed; } if( light_device.minor != MISC_DYNAMIC_MINOR ){ light_device_minor = light_device.minor; } if( proximity_device.minor != MISC_DYNAMIC_MINOR ){ proximity_device_minor = proximity_device.minor ; } wake_lock_init(&proximity_wake_lock, WAKE_LOCK_SUSPEND, "proximity"); hrtimer_init(&aps->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); aps->timer.function = aps_timer_func; aps_wq = create_singlethread_workqueue("aps_wq"); if (!aps_wq) { ret = -ENOMEM; goto err_create_workqueue_failed; } this_aps_data =aps; #ifdef CONFIG_HAS_EARLYSUSPEND aps->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; aps->early_suspend.suspend = aps_12d_early_suspend; aps->early_suspend.resume = aps_12d_early_resume; register_early_suspend(&aps->early_suspend); #endif #ifdef CONFIG_HUAWEI_HW_DEV_DCT /* detect current device successful, set the flag as present */ set_hw_dev_flag(DEV_I2C_APS); #endif printk(KERN_INFO "aps_12d_probe: Start Proximity Sensor APS-12D\n"); #ifdef CONFIG_MELFAS_UPDATE_TS_FIRMWARE TS_updateFW_aps_data = this_aps_data; TS_updateFW_aps_wq = aps_wq; #endif return 0; err_create_workqueue_failed: misc_deregister(&proximity_device); err_proximity_misc_device_register_failed: misc_deregister(&light_device); err_light_misc_device_register_failed: err_input_register_device_failed: input_free_device(aps->input_dev); err_input_dev_alloc_failed: err_detect_failed: kfree(aps); err_alloc_data_failed: err_check_functionality_failed: return ret; }
static int aps_12d_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; short flag; switch (cmd) { case ECS_IOCTL_APP_SET_LFLAG: if (copy_from_user(&flag, argp, sizeof(flag))) return -EFAULT; break; case ECS_IOCTL_APP_SET_PFLAG: if (copy_from_user(&flag, argp, sizeof(flag))) return -EFAULT; break; case ECS_IOCTL_APP_SET_DELAY: if (copy_from_user(&flag, argp, sizeof(flag))) return -EFAULT; break; default: break; } switch (cmd) { case ECS_IOCTL_APP_SET_LFLAG: atomic_set(&l_flag, flag); break; case ECS_IOCTL_APP_GET_LFLAG: /*get open acceleration sensor flag*/ flag = atomic_read(&l_flag); break; case ECS_IOCTL_APP_SET_PFLAG: atomic_set(&p_flag, flag); if( machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150() || machine_is_msm7x25_c8510() \ || machine_is_msm7x25_u8160() || machine_is_msm7x25_u8130() || machine_is_msm7x25_u8159()) { if( flag ) { /* * this means the proximity sensor is open. * so init the range_index to zero */ range_index = 0; #ifdef DEBUG_AUTO_RANGE_ADJUST printk("reinit range_index to 0!\n"); #endif } } break; case ECS_IOCTL_APP_GET_PFLAG: /*get open acceleration sensor flag*/ flag = atomic_read(&p_flag); break; case ECS_IOCTL_APP_SET_DELAY: if(flag) aps_12d_delay = flag; else aps_12d_delay = 200; /*200ms*/ break; case ECS_IOCTL_APP_GET_DELAY: flag = aps_12d_delay; break; default: break; } switch (cmd) { case ECS_IOCTL_APP_GET_LFLAG: if (copy_to_user(argp, &flag, sizeof(flag))) return -EFAULT; break; case ECS_IOCTL_APP_GET_PFLAG: if (copy_to_user(argp, &flag, sizeof(flag))) return -EFAULT; break; case ECS_IOCTL_APP_GET_DELAY: if (copy_to_user(argp, &flag, sizeof(flag))) return -EFAULT; break; default: break; } return 0; }
static void aps_12d_work_func(struct work_struct *work) { int flag = -1; // delete flag_old int ret; int reg_val_lsb; int reg_val_msb; int sesc = aps_12d_delay/1000; int nsesc = (aps_12d_delay%1000)*1000000; int ir_count = 0; int ps_count = 0; uint16_t als_count = 0; uint8_t als_level = 0; /* del als_level_old */ uint8_t i; struct aps_data *aps = container_of(work, struct aps_data, work); PROXIMITY_DEBUG("aps_12d_work_func enter\n "); if (atomic_read(&p_flag)) { /* Command 1 register: IR once */ adjust_time = 0; re_adjust: if( machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150() || machine_is_msm7x25_c8510() \ || machine_is_msm7x25_u8160() || machine_is_msm7x25_u8130() || machine_is_msm7x25_u8159()) { /* init the range to the num last time we set */ if(( range_index >=0 ) && ( range_index < TOTAL_RANGE_NUM )) { aps_i2c_reg_write(aps, APS_12D_REG_CMD2, \ (uint8_t)(APS_12D_IRDR_SEL_50MA << 6 | \ APS_12D_FREQ_SEL_DC << 4 | \ APS_12D_RES_SEL_12 << 2 | \ range_reg_value[range_index])); high_threshold = high_threshold_value[range_index]; low_threshold = low_threshold_value[range_index]; #ifdef DEBUG_AUTO_RANGE_ADJUST // del printk #endif } else { printk("BUG: range_index error!!!!\n"); range_index = 0; } } ret = aps_i2c_reg_write(aps, APS_12D_REG_CMD1, APS_12D_IR_ONCE); msleep(60); reg_val_lsb = aps_i2c_reg_read(aps, APS_12D_DATA_LSB); reg_val_msb = aps_i2c_reg_read(aps, APS_12D_DATA_MSB); ir_count = ((uint16_t)reg_val_msb << 8) + (uint16_t)reg_val_lsb; PROXIMITY_DEBUG("IR once lsb=%d; msb=%d; ir_count=%d \n", reg_val_lsb, reg_val_msb, ir_count); if (ir_count > 0xFFF){ PROXIMITY_DEBUG("get wrong ir value, ir_count=%d \n", ir_count); ir_count = 0xFFF; } if (ir_count < 0){ PROXIMITY_DEBUG("get wrong ir value, ir_count=%d \n", ir_count); ir_count = 0; } /* * auto adjust the range * stratety: * if current adc value >= up_range_value[i] * switch to upper range * if current adc value < down_range_value[i] * switch to lower range */ if( machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150() || machine_is_msm7x25_c8510() \ || machine_is_msm7x25_u8160() || machine_is_msm7x25_u8130() || machine_is_msm7x25_u8159()) { if(ir_count > up_range_value[range_index]) { if(adjust_time < 3) { if(range_index < TOTAL_RANGE_NUM-1) { #ifdef DEBUG_AUTO_RANGE_ADJUST printk("switch to upper range: %d.\n", range_index); #endif range_index++; adjust_time++; goto re_adjust; } else { printk("infrared ray TOO HIGH?\n"); } } else { printk("proximity readjust exceed max retry times.\n"); } } else if((ir_count < down_range_value[range_index])) { if(adjust_time < 3) { if(range_index >=1) { #ifdef DEBUG_AUTO_RANGE_ADJUST printk("switch to lower range: %d.\n", range_index); #endif range_index--; adjust_time++; goto re_adjust; } else { printk("BUG: no exist lux value!!\n"); } } else { printk("proximity readjust exceed max retry times.\n"); } } } msleep(30); ret = aps_i2c_reg_write(aps, APS_12D_REG_CMD1, APS_12D_PROXIMITY_ONCE); msleep(60); reg_val_lsb = aps_i2c_reg_read(aps, APS_12D_DATA_LSB); reg_val_msb = aps_i2c_reg_read(aps, APS_12D_DATA_MSB); ps_count = ((uint16_t)reg_val_msb << 8) + (uint16_t)reg_val_lsb; PROXIMITY_DEBUG("PS once lsb=%d; msb=%d; ps_count=%d \n", reg_val_lsb, reg_val_msb, ps_count); if (ps_count > 0xFFF){ PROXIMITY_DEBUG("get wrong ps value, ps_count=%d \n", ps_count); ps_count = 0xFFF; } if (ps_count < 0){ PROXIMITY_DEBUG("get wrong ps value, ps_count=%d \n", ps_count); ps_count = 0; } if((ps_count - ir_count) > high_threshold) flag = 0; else if( (ps_count - ir_count) < low_threshold ) flag = 1; else{ PROXIMITY_DEBUG("the value is in the threshold, do not report. \n"); } if( machine_is_msm7x25_u8150() || machine_is_msm7x25_c8150() || machine_is_msm7x25_c8510() \ || machine_is_msm7x25_u8160() || machine_is_msm7x25_u8130() || machine_is_msm7x25_u8159()) { /* skip invalid event */ if(-1 != flag) { if(1 == flag) { /* report far event immediately */ /* 0 is close, 1 is far */ input_report_abs(aps->input_dev, ABS_DISTANCE, flag); input_sync(aps->input_dev); } else if(last_event != flag) { printk("NOTE: skip unstable data: %s !!!\n", flag ? "far" : "close"); } else { PROXIMITY_DEBUG("report distance flag=%d \n", flag); /* 0 is close, 1 is far */ input_report_abs(aps->input_dev, ABS_DISTANCE, flag); input_sync(aps->input_dev); } } /* save the last event */ last_event = flag; /* * reset the sensor range to 1000. */ if(0 != range_index) { aps_i2c_reg_write(aps, APS_12D_REG_CMD2, \ (uint8_t)(APS_12D_IRDR_SEL_50MA << 6 | \ APS_12D_FREQ_SEL_DC << 4 | \ APS_12D_RES_SEL_12 << 2 | \ APS_12D_RANGE_SEL_ALS_1000)); #ifdef DEBUG_AUTO_RANGE_ADJUST printk("reset range!!\n"); #endif } } else { PROXIMITY_DEBUG("report distance flag=%d \n", flag); /* 0 is close, 1 is far */ input_report_abs(aps->input_dev, ABS_DISTANCE, flag); input_sync(aps->input_dev); } } if (atomic_read(&p_flag) && atomic_read(&l_flag)) msleep(120); if (atomic_read(&l_flag)) { ret = aps_i2c_reg_write(aps, APS_12D_REG_CMD1, APS_12D_ALS_ONCE); msleep(60); reg_val_lsb = aps_i2c_reg_read(aps, APS_12D_DATA_LSB); reg_val_msb = aps_i2c_reg_read(aps, APS_12D_DATA_MSB); als_count = ((uint16_t)reg_val_msb << 8) + (uint16_t)reg_val_lsb; PROXIMITY_DEBUG("ALS once lsb=%d; msb=%d; als_count=%d \n", reg_val_lsb, reg_val_msb, als_count); if (als_count > 0xFFF){ PROXIMITY_DEBUG("get wrong als value, als_count=%d \n", als_count); als_count = 0xFFF; } als_level = LSENSOR_MAX_LEVEL - 1; for (i = 0; i < ARRAY_SIZE(lsensor_adc_table); i++){ if (als_count < lsensor_adc_table[i]){ als_level = i; break; } } PROXIMITY_DEBUG("report adc level=%d \n", als_level); if(aps_first_read) { /* report a invalid key first */ aps_first_read = 0; input_report_abs(aps->input_dev, ABS_LIGHT, -1); input_sync(aps->input_dev); } else { PROXIMITY_DEBUG("report lux value=%d \n", lsensor_lux_table[als_level]); input_report_abs(aps->input_dev, ABS_LIGHT, lsensor_lux_table[als_level]); input_sync(aps->input_dev); } } if (atomic_read(&p_flag) || atomic_read(&l_flag)) hrtimer_start(&aps->timer, ktime_set(sesc, nsesc), HRTIMER_MODE_REL); }
static irqreturn_t msm_i2c_interrupt(int irq, void *devid) { struct msm_i2c_dev *dev = devid; uint32_t status = readl(dev->base + I2C_STATUS); int err = 0; #if DEBUG dump_status(status); #endif spin_lock(&dev->lock); if (!dev->msg) { printk(KERN_ERR "%s: IRQ but nothing to do!\n", __func__); spin_unlock(&dev->lock); return IRQ_HANDLED; } if (status & I2C_STATUS_ERROR_MASK) { err = -EIO; goto out_err; } if (dev->msg->flags & I2C_M_RD) { if (status & I2C_STATUS_RD_BUFFER_FULL) { /* * Theres something in the FIFO. * Are we expecting data or flush crap? */ if (dev->cnt) { /* DATA */ uint8_t *data = &dev->msg->buf[dev->pos]; /* This is in spin-lock. So there will be no * scheduling between reading the second-last * byte and writing LAST_BYTE to the controller. * So extra read-cycle-clock won't be generated * Per I2C MSM HW Specs: Write LAST_BYTE befure * reading 2nd last byte */ if (dev->cnt == 2) writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA); *data = readl(dev->base + I2C_READ_DATA); dev->cnt--; dev->pos++; if (dev->msg->len == 1) dev->rd_acked = 0; if (dev->cnt == 0) goto out_complete; } else { /* Now that extra read-cycle-clocks aren't * generated, this becomes error condition */ dev_err(dev->dev, "read did not stop, status - %x\n", status); err = -EIO; goto out_err; } } else if (dev->msg->len == 1 && dev->rd_acked == 0 && ((status & I2C_STATUS_RX_DATA_STATE) == I2C_STATUS_RX_DATA_STATE)) writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA); } else { uint16_t data; if (status & I2C_STATUS_WR_BUFFER_FULL) { dev_err(dev->dev, "Write buffer full in ISR on write?\n"); err = -EIO; goto out_err; } if(machine_is_msm7x25_u8150() && (dev->msg->addr == 0x1E))//st303_compass address 0x1e { //printk(KERN_ERR "msg->addr=:0x%x \n",dev->msg->addr); udelay(8); } if (dev->cnt) { /* Ready to take a byte */ data = dev->msg->buf[dev->pos]; if (dev->cnt == 1 && dev->rem == 1) data |= I2C_WRITE_DATA_LAST_BYTE; writel(data, dev->base + I2C_WRITE_DATA); dev->pos++; dev->cnt--; } else goto out_complete; } spin_unlock(&dev->lock); return IRQ_HANDLED; out_err: dev->err = err; out_complete: complete(dev->complete); spin_unlock(&dev->lock); return IRQ_HANDLED; }