diff options
Diffstat (limited to 'drivers')
27 files changed, 1402 insertions, 232 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 01e21037d8f..d7872b96019 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -27,6 +27,7 @@ #include <linux/pm.h> #include <linux/suspend.h> #include <linux/syscore_ops.h> +#include <linux/reboot.h> #include <generated/utsrelease.h> @@ -130,6 +131,7 @@ struct firmware_buf { struct page **pages; int nr_pages; int page_array_size; + struct list_head pending_list; #endif char fw_id[]; }; @@ -171,6 +173,9 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); +#ifdef CONFIG_FW_LOADER_USER_HELPER + INIT_LIST_HEAD(&buf->pending_list); +#endif pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -446,10 +451,8 @@ static struct firmware_priv *to_firmware_priv(struct device *dev) return container_of(dev, struct firmware_priv, dev); } -static void fw_load_abort(struct firmware_priv *fw_priv) +static void __fw_load_abort(struct firmware_buf *buf) { - struct firmware_buf *buf = fw_priv->buf; - /* * There is a small window in which user can write to 'loading' * between loading done and disappearance of 'loading' @@ -457,8 +460,16 @@ static void fw_load_abort(struct firmware_priv *fw_priv) if (test_bit(FW_STATUS_DONE, &buf->status)) return; + list_del_init(&buf->pending_list); set_bit(FW_STATUS_ABORT, &buf->status); complete_all(&buf->completion); +} + +static void fw_load_abort(struct firmware_priv *fw_priv) +{ + struct firmware_buf *buf = fw_priv->buf; + + __fw_load_abort(buf); /* avoid user action after loading abort */ fw_priv->buf = NULL; @@ -467,6 +478,25 @@ static void fw_load_abort(struct firmware_priv *fw_priv) #define is_fw_load_aborted(buf) \ test_bit(FW_STATUS_ABORT, &(buf)->status) +static LIST_HEAD(pending_fw_head); + +/* reboot notifier for avoid deadlock with usermode_lock */ +static int fw_shutdown_notify(struct notifier_block *unused1, + unsigned long unused2, void *unused3) +{ + mutex_lock(&fw_lock); + while (!list_empty(&pending_fw_head)) + __fw_load_abort(list_first_entry(&pending_fw_head, + struct firmware_buf, + pending_list)); + mutex_unlock(&fw_lock); + return NOTIFY_DONE; +} + +static struct notifier_block fw_shutdown_nb = { + .notifier_call = fw_shutdown_notify, +}; + static ssize_t firmware_timeout_show(struct class *class, struct class_attribute *attr, char *buf) @@ -619,6 +649,7 @@ static ssize_t firmware_loading_store(struct device *dev, * is completed. * */ fw_map_pages_buf(fw_buf); + list_del_init(&fw_buf->pending_list); complete_all(&fw_buf->completion); break; } @@ -853,8 +884,15 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, goto err_del_dev; } + mutex_lock(&fw_lock); + list_add(&buf->pending_list, &pending_fw_head); + mutex_unlock(&fw_lock); + retval = device_create_file(f_dev, &dev_attr_loading); if (retval) { + mutex_lock(&fw_lock); + list_del_init(&buf->pending_list); + mutex_unlock(&fw_lock); dev_err(f_dev, "%s: device_create_file failed\n", __func__); goto err_del_bin_attr; } @@ -1526,6 +1564,7 @@ static int __init firmware_class_init(void) { fw_cache_init(); #ifdef CONFIG_FW_LOADER_USER_HELPER + register_reboot_notifier(&fw_shutdown_nb); return class_register(&firmware_class); #else return 0; @@ -1539,6 +1578,7 @@ static void __exit firmware_class_exit(void) unregister_pm_notifier(&fw_cache.pm_notify); #endif #ifdef CONFIG_FW_LOADER_USER_HELPER + unregister_reboot_notifier(&fw_shutdown_nb); class_unregister(&firmware_class); #endif } diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index dd7bc175ca8..0b9f6a3c66c 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -150,15 +150,22 @@ out: static int cpu0_cpufreq_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) { + static unsigned int old_policy_max; mutex_lock(&cpu0_cpufreq_lock); if (event == PM_SUSPEND_PREPARE) { struct cpufreq_policy *policy = cpufreq_cpu_get(0); is_suspended = true; + old_policy_max = policy->max; + policy->max = policy->cpuinfo.max_freq; pr_debug("cpu0 cpufreq suspend: setting frequency to %d kHz\n", policy->max); __cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); cpufreq_cpu_put(policy); } else if (event == PM_POST_SUSPEND) { + struct cpufreq_policy *policy = cpufreq_cpu_get(0); + policy->max = old_policy_max; + __cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); + cpufreq_cpu_put(policy); is_suspended = false; } mutex_unlock(&cpu0_cpufreq_lock); @@ -182,6 +189,7 @@ static int cpu0_cpufreq_reboot_notify(struct notifier_block *nb, mutex_lock(&cpu0_cpufreq_lock); policy = cpufreq_cpu_get(0); is_suspended = true; + policy->max = policy->cpuinfo.max_freq; pr_info("cpu0 cpufreq shutdown: setting frequency to %d kHz\n", policy->max); __cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); diff --git a/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c b/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c index c9b577eddbe..f13d0cfa586 100755 --- a/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c +++ b/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c @@ -3169,7 +3169,7 @@ PVRSRV_ERROR OSEventObjectSignalKM(IMG_HANDLE hOSEventKM) ******************************************************************************/ IMG_BOOL OSProcHasPrivSrvInit(IMG_VOID) { - return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE; + return (capable(CAP_SYS_ADMIN) != 0) ? IMG_TRUE : IMG_FALSE; } /*! diff --git a/drivers/leds/leds-lm3535.c b/drivers/leds/leds-lm3535.c index 3f157942803..23b7558674e 100644 --- a/drivers/leds/leds-lm3535.c +++ b/drivers/leds/leds-lm3535.c @@ -50,10 +50,11 @@ #ifdef CONFIG_LM3535_ESD_RECOVERY #include <mot/esd_poll.h> #endif /* CONFIG_LM3535_ESD_RECOVERY */ -#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY #include <linux/notifier.h> +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +#include <linux/als_notify.h> #include <linux/wakeup_source_notify.h> -#define MIN_DOCK_BVALUE 36 +#define MIN_DOCK_BVALUE 36 #include <linux/m4sensorhub.h> #include <linux/m4sensorhub/MemMapUserSettings.h> #endif @@ -273,7 +274,12 @@ struct lm3535 { int prevent_als_read; /* Whether to prevent als reads for a time */ #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY atomic_t docked; + atomic_t alsstatus; +#ifdef CONFIG_ALS_WHILE_CHARGING + atomic_t interactive; +#endif struct notifier_block dock_nb; + struct notifier_block als_nb; #endif }; static DEFINE_MUTEX(lm3535_mutex); @@ -574,30 +580,34 @@ static uint8_t lm3535_convert_value (unsigned value, unsigned zone) reg = res / als_denom; #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY - if (!lm3535_data.prevent_als_read) { - /* make sure this is atleast as high as corresponding ambient - * mode value for current ALS condition */ - m4sensorhub = m4sensorhub_client_get_drvdata(); - size = m4sensorhub_reg_getsize(m4sensorhub, - M4SH_REG_LIGHTSENSOR_SIGNAL); - if (size != sizeof(als)) { - pr_err("can't get M4 reg size for ALS\n"); - ambient_als_backlight = 0; - } else if (size != m4sensorhub_reg_read(m4sensorhub, - M4SH_REG_LIGHTSENSOR_SIGNAL, - (char *)&als)) { - pr_err("error reading M4 ALS value\n"); - ambient_als_backlight = 0; - } else { - adjust_als = true; - /* prevent als reads for next 500 ms */ - lm3535_data.prevent_als_read = 1; - schedule_delayed_work(&lm3535_data.als_delayed_work, - msecs_to_jiffies(500)); + if (atomic_read(&lm3535_data.alsstatus)) { + if (!lm3535_data.prevent_als_read) { + /* make sure this is atleast as + high as corresponding ambient + * mode value for current ALS condition */ + m4sensorhub = m4sensorhub_client_get_drvdata(); + size = m4sensorhub_reg_getsize(m4sensorhub, + M4SH_REG_LIGHTSENSOR_SIGNAL); + if (size != sizeof(als)) { + pr_err("can't get M4 reg size for ALS\n"); + ambient_als_backlight = 0; + } else if (size != m4sensorhub_reg_read(m4sensorhub, + M4SH_REG_LIGHTSENSOR_SIGNAL, + (char *)&als)) { + pr_err("error reading M4 ALS value\n"); + ambient_als_backlight = 0; + } else { + adjust_als = true; + /* prevent als reads for next 500 ms */ + lm3535_data.prevent_als_read = 1; + schedule_delayed_work( + &lm3535_data.als_delayed_work, + msecs_to_jiffies(500)); + } + } else if (ambient_als_backlight > reg) { + /* If valid, use previously read als value */ + reg = ambient_als_backlight; } - } else if (ambient_als_backlight > reg) { - /* If valid, use previously read als value */ - reg = ambient_als_backlight; } if (adjust_als) { @@ -606,7 +616,7 @@ static uint8_t lm3535_convert_value (unsigned value, unsigned zone) if (ambient_als_backlight > reg) reg = ambient_als_backlight; } -#endif +#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY*/ printk_br(KERN_INFO "%s: v=%d, z=%d, res=0x%x, reg=0x%x\n", __func__, value, zone, res, reg); @@ -660,6 +670,31 @@ static void lm3535_brightness_set_raw_als(struct led_classdev *led_cdev, mutex_unlock(&lm3535_mutex); } #endif + +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +static int lm3535_als_notifier(struct notifier_block *self, + unsigned long action, void *dev) +{ + pr_info("%s: ALS value is %lu\n", __func__, action); + switch (action) { + case ALS_ENABLED: + case ALS_DISABLED: + atomic_set(&lm3535_data.use_als, (action == ALS_ENABLED)); + break; + default: +#ifdef CONFIG_ALS_WHILE_CHARGING + if (atomic_read(&lm3535_data.interactive) == 0) + lm3535_brightness_set_raw_als(led_get_default_dev(), + (unsigned int)action); + else + pr_info("%s: ignoring ALS notifications\n", __func__); +#endif + break; + } + return NOTIFY_OK; +} +#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ + static void lm3535_brightness_set (struct led_classdev *led_cdev, enum led_brightness value) { @@ -707,6 +742,7 @@ static void lm3535_brightness_set (struct led_classdev *led_cdev, /* Calculate brightness value for each zone relative to its cap */ bvalue = lm3535_convert_value (value, bright_zone); + #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY if (atomic_read(&lm3535_data.docked) && (bvalue < MIN_DOCK_BVALUE)) bvalue = MIN_DOCK_BVALUE; /* hard code for dock mode */ @@ -922,6 +958,38 @@ static ssize_t lm3535_suspend_store (struct device *dev, } static DEVICE_ATTR(suspend, 0644, lm3535_suspend_show, lm3535_suspend_store); +#ifdef CONFIG_ALS_WHILE_CHARGING +static ssize_t lm3535_interactive_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", atomic_read(&lm3535_data.interactive)); +} + +static ssize_t lm3535_interactive_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned value = 0; + + if (!buf || size == 0) { + pr_err("%s: invalid command\n", __func__); + return -EINVAL; + } + + sscanf(buf, "%d", &value); + if (value) + atomic_set(&lm3535_data.interactive, 1); + else + atomic_set(&lm3535_data.interactive, 0); + + return size; +} +static DEVICE_ATTR(interactive, S_IRUGO | S_IWUSR, + lm3535_interactive_show, + lm3535_interactive_store); +#endif + /* This function is called by i2c_probe */ static int lm3535_probe (struct i2c_client *client, const struct i2c_device_id *id) @@ -997,6 +1065,18 @@ static int lm3535_probe (struct i2c_client *client, misc_deregister (&als_miscdev); return ret; } +#ifdef CONFIG_ALS_WHILE_CHARGING + ret = device_create_file(lm3535_led.dev, &dev_attr_interactive); + if (ret) { + pr_err("err creating interactive file for %s: %d\n", + lm3535_led.name, ret); + led_classdev_unregister(&lm3535_led); + led_classdev_unregister(&lm3535_led_noramp); + device_remove_file(lm3535_led.dev, &dev_attr_suspend); + misc_deregister(&als_miscdev); + return ret; + } +#endif dev_set_drvdata (lm3535_led.dev, &lm3535_led); #if 0 lm3535_data.idev = input_allocate_device(); @@ -1029,15 +1109,23 @@ static int lm3535_probe (struct i2c_client *client, register_early_suspend (&early_suspend_data); #endif - lm3535_led.brightness = 255; - lm3535_led_noramp.brightness = 255; - //lm3535_brightness_set (&lm3535_led_noramp, 255); - lm3535_write_reg (LM3535_BRIGHTNESS_CTRL_REG_A, 0x79, __FUNCTION__); - lm3535_data.initialized = 1; + lm3535_led.brightness = 84; + lm3535_led_noramp.brightness = 84; + /* lm3535_brightness_set (&lm3535_led_noramp, 255); */ + lm3535_write_reg(LM3535_BRIGHTNESS_CTRL_REG_A, 87, __func__); + lm3535_data.initialized = 1; #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY atomic_set(&lm3535_data.docked, 0); + /* default setting for minnow is to use ALS */ + atomic_set(&lm3535_data.alsstatus, 1); +#ifdef CONFIG_ALS_WHILE_CHARGING + atomic_set(&lm3535_data.interactive, 1); +#endif lm3535_data.dock_nb.notifier_call = lm3535_dock_notifier; wakeup_source_register_notify(&lm3535_data.dock_nb); + + lm3535_data.als_nb.notifier_call = lm3535_als_notifier; + als_register_notify(&lm3535_data.als_nb); #endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ INIT_DELAYED_WORK(&lm3535_data.als_delayed_work, @@ -1515,10 +1603,18 @@ static int lm3535_remove (struct i2c_client *client) /* led_classdev_unregister (&lm3535_led_noramp); */ misc_deregister (&als_miscdev); device_remove_file (lm3535_led.dev, &dev_attr_suspend); +#ifdef CONFIG_ALS_WHILE_CHARGING + device_remove_file(lm3535_led.dev, &dev_attr_interactive); +#endif #if 0 input_unregister_device (lm3535_data.idev); input_free_device (lm3535_data.idev); #endif + +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY + wakeup_source_unregister_notify(&lm3535_data.dock_nb); + als_unregister_notify(&lm3535_data.als_nb); +#endif return 0; } diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c0704d70b97..e14fcb9363f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -171,6 +171,7 @@ m4sensorhub-objs := m4sensorhub-core.o \ m4sensorhub-reg.o \ m4sensorhub-irq.o \ m4sensorhub-panic.o \ + m4sensorhub-extern.o \ m4sensorhub-stm32-fw.o \ m4sensorhub-stm32_401-fw.o diff --git a/drivers/mfd/m4sensorhub-core.c b/drivers/mfd/m4sensorhub-core.c index a10ab20280b..203109e6ac7 100644 --- a/drivers/mfd/m4sensorhub-core.c +++ b/drivers/mfd/m4sensorhub-core.c @@ -90,6 +90,12 @@ static ssize_t m4sensorhub_get_dbg(struct device *dev, /* BEGIN BOARD FILE */ /* TODO: replace with request array */ +int m4sensorhub_get_current_mode(void) +{ + return m4sensorhub_misc_data.mode; +} +EXPORT_SYMBOL_GPL(m4sensorhub_get_current_mode); + int m4sensorhub_set_bootmode(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_bootmode bootmode) { @@ -496,6 +502,12 @@ static void m4sensorhub_initialize(const struct firmware *firmware, return; } + err = m4sensorhub_extern_init(&m4sensorhub_misc_data); + if (err < 0) { + KDEBUG(M4SH_ERROR, "%s: Extern init failed.\n", __func__); + return; + } + /* Initialize all the m4 drivers */ inc = inithead; arg.p_m4sensorhub_data = &m4sensorhub_misc_data; @@ -540,20 +552,27 @@ static DEVICE_ATTR(debug_level, S_IRUSR|S_IWUSR, m4sensorhub_get_dbg, static ssize_t m4sensorhub_get_loglevel(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned long long loglevel; + uint32_t logenable[LOG_EN_SIZE], len, i; m4sensorhub_reg_read(&m4sensorhub_misc_data, - M4SH_REG_LOG_LOGENABLE, (char *)&loglevel); - KDEBUG(M4SH_INFO, "M4 loglevel = %llx", loglevel); - return sprintf(buf, "%llu\n", loglevel); + M4SH_REG_LOG_LOGENABLE, (char *)&logenable); + + len = sprintf(buf, "M4 log levels = 0x"); + for (i = 0; i < LOG_EN_SIZE; i++) + len += sprintf(buf+len, "%08x", logenable[i]); + KDEBUG(M4SH_INFO, "%s\n", buf); + return snprintf(buf, PAGE_SIZE, "%s\n", buf); } -void m4sensorhub_update_loglevels(char *tag, char *level, - unsigned long long *log_levels) +void m4sensorhub_update_loglevels(int8_t *tag, int8_t *level, + uint32_t *log_level) { - int i; - int levelindex = -1; - int tagindex = -1; - unsigned long long mask; + uint32_t i; + int32_t levelindex = -1; + int32_t tagindex = -1; + uint32_t mask; + int32_t logenableindex; + int32_t en = LOG_TAGS_PER_ENABLE; + int32_t b = LOG_NO_OF_BITS_PER_TAG; for (i = 0; i < LOG_LEVELS_MAX; i++) { if (strcmp(acLogLevels[i], level) == 0) { @@ -568,16 +587,21 @@ void m4sensorhub_update_loglevels(char *tag, char *level, break; } } - if ((tagindex == -1) || (levelindex == -1)) return; + logenableindex = tagindex/LOG_TAGS_PER_ENABLE; + /*Clear the revelant bits*/ - mask = 0x03; - *log_levels &= ~(mask << (tagindex * 2)); + mask = LOG_TAG_MASK; + *(log_level+logenableindex) &= ~(mask << ((tagindex % en) * b)); /*set debug level for the relevant bits*/ - *log_levels |= (levelindex << (tagindex * 2)); - KDEBUG(M4SH_INFO, "New M4 log levels = 0x%llx\n", *log_levels); + *(log_level+logenableindex) |= (levelindex << ((tagindex % en) * b)); + + KDEBUG(M4SH_DEBUG, "New M4 log levels = "); + for (i = 0; i < LOG_EN_SIZE; i++) + KDEBUG(M4SH_DEBUG, "enable %d = 0x%08x ", i, *(log_level+i)); + KDEBUG(M4SH_DEBUG, "\n"); } /* Usage: adb shell into the directory of sysinterface log_level and @@ -585,25 +609,29 @@ void m4sensorhub_update_loglevels(char *tag, char *level, static ssize_t m4sensorhub_set_loglevel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long long cur_loglevels; + uint32_t cur_loglevels[LOG_EN_SIZE]; char *tag, *level; char **logbuf = (char **) &buf; m4sensorhub_reg_read(&m4sensorhub_misc_data, - M4SH_REG_LOG_LOGENABLE, (char *)&cur_loglevels); + M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels); + while (1) { tag = strsep(logbuf, "=,\n "); - if (tag == NULL) + if ((tag == NULL) || (logbuf == NULL)) break; level = strsep(logbuf, "=,\n "); - if (level == NULL) + if ((level == NULL) || (logbuf == NULL)) break; - m4sensorhub_update_loglevels(tag, level, &cur_loglevels); + m4sensorhub_update_loglevels(tag, level, + (uint32_t *)cur_loglevels); } - return m4sensorhub_reg_write(&m4sensorhub_misc_data, - M4SH_REG_LOG_LOGENABLE, (char *)&cur_loglevels, - m4sh_no_mask); + m4sensorhub_reg_write(&m4sensorhub_misc_data, + M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels, + m4sh_no_mask); + + return count; } static DEVICE_ATTR(log_level, S_IRUSR|S_IWUSR, m4sensorhub_get_loglevel, diff --git a/drivers/mfd/m4sensorhub-extern.c b/drivers/mfd/m4sensorhub-extern.c new file mode 100644 index 00000000000..771dc5cc4f3 --- /dev/null +++ b/drivers/mfd/m4sensorhub-extern.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Adds ability to program periodic interrupts from user space that + * can wake the phone out of low power modes. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/m4sensorhub.h> +#include <linux/slab.h> + +#define m4ext_err(format, args...) KDEBUG(M4SH_ERROR, format, ## args) + +struct m4sensorhub_data *m4ext_m4; +DEFINE_MUTEX(m4ext_mutex); +uint8_t m4ext_display_status; +uint8_t m4ext_audio_status; + +static void m4ext_panic_callback(struct m4sensorhub_data *m4, void *context) +{ + int err; + + mutex_lock(&m4ext_mutex); + + if (m4ext_m4 == NULL) { + m4ext_err("%s: M4 data is NULL.\n", __func__); + err = -ENODATA; + goto m4ext_panic_callback_fail; + } + + err = m4sensorhub_reg_write(m4, M4SH_REG_USERSETTINGS_SCREENSTATUS, + (char *)&m4ext_display_status, m4sh_no_mask); + if (err < 0) + m4ext_err("%s: Screen status write failed (%d).\n", + __func__, err); + else if (err != 1) + m4ext_err("%s: Screen status wrote %d bytes instead of 1.\n", + __func__, err); + + err = m4sensorhub_reg_write(m4, M4SH_REG_USERSETTINGS_AUDIOSTATUS, + (char *)&m4ext_audio_status, m4sh_no_mask); + if (err < 0) + m4ext_err("%s: Audio status write failed (%d).\n", + __func__, err); + else if (err != 1) + m4ext_err("%s: Audio status wrote %d bytes instead of 1.\n", + __func__, err); + +m4ext_panic_callback_fail: + mutex_unlock(&m4ext_mutex); + return; +} + +int m4sensorhub_extern_init(struct m4sensorhub_data *m4) +{ + int err; + + mutex_lock(&m4ext_mutex); + + err = m4sensorhub_panic_register(m4, PANICHDL_EXTERN_RESTORE, + m4ext_panic_callback, NULL); + if (err < 0) { + m4ext_err("%s: Failed to register panic callback.\n", __func__); + goto m4sensorhub_extern_init_fail; + } + + m4ext_m4 = m4; + +m4sensorhub_extern_init_fail: + mutex_unlock(&m4ext_mutex); + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_init); + +int m4sensorhub_extern_set_display_status(uint8_t status) +{ + int err; + + mutex_lock(&m4ext_mutex); + + if (m4ext_m4 == NULL) { + m4ext_err("%s: M4 data is NULL.\n", __func__); + err = -ENODATA; + goto m4sensorhub_extern_set_display_status_fail; + } + + m4ext_display_status = status; + + err = m4sensorhub_reg_write(m4ext_m4, + M4SH_REG_USERSETTINGS_SCREENSTATUS, + (char *)&status, m4sh_no_mask); + if (err < 0) { + m4ext_err("%s: I2C write failed (%d).\n", __func__, err); + goto m4sensorhub_extern_set_display_status_fail; + } else if (err != 1) { + m4ext_err("%s: Wrote %d bytes instead of 1.\n", __func__, err); + err = -EINVAL; + goto m4sensorhub_extern_set_display_status_fail; + } + + err = 0; + +m4sensorhub_extern_set_display_status_fail: + mutex_unlock(&m4ext_mutex); + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_set_display_status); + +int m4sensorhub_extern_set_audio_status(uint8_t status) +{ + int err; + + mutex_lock(&m4ext_mutex); + + if (m4ext_m4 == NULL) { + m4ext_err("%s: M4 data is NULL.\n", __func__); + err = -ENODATA; + goto m4sensorhub_extern_set_audio_status_fail; + } + + m4ext_audio_status = status; + + err = m4sensorhub_reg_write(m4ext_m4, + M4SH_REG_USERSETTINGS_AUDIOSTATUS, + (char *)&status, m4sh_no_mask); + if (err < 0) { + m4ext_err("%s: I2C write failed (%d).\n", __func__, err); + goto m4sensorhub_extern_set_audio_status_fail; + } else if (err != 1) { + m4ext_err("%s: Wrote %d bytes instead of 1.\n", __func__, err); + err = -EINVAL; + goto m4sensorhub_extern_set_audio_status_fail; + } + + err = 0; + +m4sensorhub_extern_set_audio_status_fail: + mutex_unlock(&m4ext_mutex); + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_set_audio_status); diff --git a/drivers/mfd/m4sensorhub-panic.c b/drivers/mfd/m4sensorhub-panic.c index b127adeba5c..34ad6658453 100644 --- a/drivers/mfd/m4sensorhub-panic.c +++ b/drivers/mfd/m4sensorhub-panic.c @@ -40,6 +40,7 @@ static const char *callback_name[PANICHDL_MAX] = { [PANICHDL_FUSION_RESTORE] = "fusion_restore", [PANICHDL_MPU9150_RESTORE] = "mpu9150_restore", [PANICHDL_PEDOMETER_RESTORE] = "pedometer_restore", + [PANICHDL_EXTERN_RESTORE] = "extern_restore", }; struct m4sensorhub_panic_callback { diff --git a/drivers/mfd/m4sensorhub-reg.h b/drivers/mfd/m4sensorhub-reg.h index 96719a08927..753ea9a8846 100644 --- a/drivers/mfd/m4sensorhub-reg.h +++ b/drivers/mfd/m4sensorhub-reg.h @@ -87,9 +87,9 @@ static const struct { [M4SH_REG_FUSION_LOCALX] = {M4SH_TYPE_FUSION, 0x10, 4}, [M4SH_REG_FUSION_LOCALY] = {M4SH_TYPE_FUSION, 0x14, 4}, [M4SH_REG_FUSION_LOCALZ] = {M4SH_TYPE_FUSION, 0x18, 4}, - [M4SH_REG_FUSION_WORLDX] = {M4SH_TYPE_FUSION, 0x1c, 4}, - [M4SH_REG_FUSION_WORLDY] = {M4SH_TYPE_FUSION, 0x20, 4}, - [M4SH_REG_FUSION_WORLDZ] = {M4SH_TYPE_FUSION, 0x24, 4}, + [M4SH_REG_FUSION_GRAVITYX] = {M4SH_TYPE_FUSION, 0x1c, 4}, + [M4SH_REG_FUSION_GRAVITYY] = {M4SH_TYPE_FUSION, 0x20, 4}, + [M4SH_REG_FUSION_GRAVITYZ] = {M4SH_TYPE_FUSION, 0x24, 4}, [M4SH_REG_FUSION_ROTATIONVECTOR] = {M4SH_TYPE_FUSION, 0x28, 16}, [M4SH_REG_FUSION_HEADING] = {M4SH_TYPE_FUSION, 0x38, 2}, [M4SH_REG_FUSION_HEADING_ACCURACY] = {M4SH_TYPE_FUSION, 0x3a, 1}, @@ -112,6 +112,8 @@ static const struct { [M4SH_REG_METS_METS] = {M4SH_TYPE_METS, 0x4, 4}, [M4SH_REG_METS_CALORIES] = {M4SH_TYPE_METS, 0x8, 4}, [M4SH_REG_METS_HEALTHYMINUTES] = {M4SH_TYPE_METS, 0xc, 4}, + [M4SH_REG_METS_METS_NO_RMR] = {M4SH_TYPE_METS, 0x10, 4}, + [M4SH_REG_METS_CALORIES_NO_RMR] = {M4SH_TYPE_METS, 0x14, 4}, [M4SH_REG_USERSETTINGS_SCREENSTATUS] = {M4SH_TYPE_USERSETTINGS, 0x0, 1}, [M4SH_REG_USERSETTINGS_USERAGE] = {M4SH_TYPE_USERSETTINGS, 0x1, 1}, [M4SH_REG_USERSETTINGS_USERGENDER] = {M4SH_TYPE_USERSETTINGS, 0x2, 1}, @@ -205,7 +207,7 @@ static const unsigned int bank_size_tbl[M4SH_TYPE__NUM] = { [M4SH_TYPE_FUSION] = 59, [M4SH_TYPE_COMPASS] = 17, [M4SH_TYPE_GYRO] = 16, - [M4SH_TYPE_METS] = 16, + [M4SH_TYPE_METS] = 24, [M4SH_TYPE_USERSETTINGS] = 8, [M4SH_TYPE_POWER] = 9, [M4SH_TYPE_LOCATION] = 14, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5b96da5fda8..5aed79aa3c4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -586,6 +586,13 @@ config WAKEUP_SOURCE_NOTIFY help Driver to allow early notification of wakeups +config ALS_WHILE_CHARGING + tristate "use ALS when on charger" + depends on WAKEUP_SOURCE_NOTIFY + help + ALS when unit is on charger + + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2f57dbac599..39d1e04e896 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_MOT_UTAG) += utag/ obj-$(CONFIG_BQ5105X_CTRL) += bq5105x_ctrl.o obj-$(CONFIG_BQ5105X_DETECT) += bq5105x_detect.o obj-$(CONFIG_WAKEUP_SOURCE_NOTIFY) += wakeup_source_notify.o +obj-$(CONFIG_WAKEUP_SOURCE_NOTIFY) += als_notify.o diff --git a/drivers/misc/als_notify.c b/drivers/misc/als_notify.c new file mode 100644 index 00000000000..cbb4f3a55cd --- /dev/null +++ b/drivers/misc/als_notify.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/notifier.h> +#include <linux/als_notify.h> + +static BLOCKING_NOTIFIER_HEAD(als_notifier_list); + +/** + * als_register_notify - register a notifier callback for triggering display init + * @nb: pointer to the notifier block for the callback events. + * + */ +void als_register_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_register(&als_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(als_register_notify); + +/** + * als_unregister_notify - unregister a notifier callback + * @nb: pointer to the notifier block for the callback events. + * + * als_register_notify() must have been previously called + * for this function to work properly. + */ +void als_unregister_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&als_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(als_unregister_notify); + +void als_notify_subscriber(unsigned long event) +{ + blocking_notifier_call_chain(&als_notifier_list, event, NULL); +} +EXPORT_SYMBOL_GPL(als_notify_subscriber); diff --git a/drivers/misc/c55_ctrl.c b/drivers/misc/c55_ctrl.c index 54ffc6e5b2b..cf763272922 100644 --- a/drivers/misc/c55_ctrl.c +++ b/drivers/misc/c55_ctrl.c @@ -143,7 +143,6 @@ static ssize_t c55_ctrl_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct c55_ctrl_data *cdata = dev_get_drvdata(dev); - struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); int mode; if (kstrtoint(buf, 10, &mode) < 0) @@ -154,7 +153,7 @@ static ssize_t c55_ctrl_enable(struct device *dev, return -EINVAL; } - if (m4sensorhub->mode != NORMALMODE) { + if (m4sensorhub_get_current_mode() != NORMALMODE) { dev_err(dev, "M4 not ready, Unable to set screen status\n"); return -EINVAL; } @@ -169,9 +168,7 @@ static ssize_t c55_ctrl_enable(struct device *dev, gpio_set_value(cdata->ap_c55_int_gpio, 1); - if (m4sensorhub_reg_write_1byte - (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, - AUDIO_STATUS_ON, 0xFF) != 1) { + if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_ON) < 0) { dev_err(dev, "Unable to set screen status to 0x01\n"); mutex_unlock(&cdata->ctrl_mutex); return -EINVAL; @@ -189,9 +186,7 @@ static ssize_t c55_ctrl_enable(struct device *dev, cdata->c55_ap_int_enabled = 0; } - if (m4sensorhub_reg_write_1byte - (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, - AUDIO_STATUS_OFF, 0xFF) != 1) { + if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_OFF) < 0) { dev_err(dev, "Unable to set screen status to 0x00\n"); mutex_unlock(&cdata->ctrl_mutex); return -EINVAL; @@ -363,7 +358,6 @@ static int c55_ctrl_remove(struct platform_device *pdev) static int c55_ctrl_suspend(struct platform_device *dev, pm_message_t state) { struct c55_ctrl_data *cdata = dev_get_drvdata(&dev->dev); - struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); if (cdata->c55_mode != C55_OFF) { dev_warn(&dev->dev, "C55 still ON when going into suspend\n"); @@ -375,9 +369,7 @@ static int c55_ctrl_suspend(struct platform_device *dev, pm_message_t state) cdata->c55_ap_int_enabled = 0; } - if (m4sensorhub_reg_write_1byte - (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, - AUDIO_STATUS_OFF, 0xFF) != 1) { + if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_OFF) < 0) { dev_err(&dev->dev, "Unable to set screen status to 0x00\n"); } diff --git a/drivers/misc/m4sensorhub_als.c b/drivers/misc/m4sensorhub_als.c index 3f61edefff2..6445d9f0181 100644 --- a/drivers/misc/m4sensorhub_als.c +++ b/drivers/misc/m4sensorhub_als.c @@ -28,12 +28,21 @@ #include <linux/m4sensorhub.h> #include <linux/input.h> #include <linux/slab.h> +#include <linux/delay.h> +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +#include <linux/notifier.h> +#include <linux/als_notify.h> +#ifdef CONFIG_ALS_WHILE_CHARGING +#include <linux/wakeup_source_notify.h> +#endif /* CONFIG_ALS_WHILE_CHARGING */ +#endif #define m4als_err(format, args...) KDEBUG(M4SH_ERROR, format, ## args) #define M4ALS_DRIVER_NAME "m4sensorhub_als" #define M4ALS_IRQ_ENABLED_BIT 0 +#define ALS_SAMPLERATE_WHILE_DOCKED 10 /* in seconds */ struct m4als_driver_data { struct platform_device *pdev; @@ -42,13 +51,39 @@ struct m4als_driver_data { struct input_dev *indev; struct delayed_work m4als_work; + /* Beware of changing this from uint16, check als_notify.h + since notifier uses values outside luminosity range for + conveying enable/disable status */ uint16_t luminosity; int16_t samplerate; int16_t latest_samplerate; int16_t fastest_rate; uint16_t status; +#ifdef CONFIG_ALS_WHILE_CHARGING + struct notifier_block charger_nb; + bool chargerstatus; +#endif }; +#ifdef CONFIG_ALS_WHILE_CHARGING +static int charger_notify(struct notifier_block *self, + unsigned long action, void *dev) +{ + struct m4als_driver_data *dd = + container_of(self, struct m4als_driver_data, charger_nb); + switch (action) { + case DISPLAY_WAKE_EVENT_DOCKON: + case DISPLAY_WAKE_EVENT_DOCKOFF: + dd->chargerstatus = (action == DISPLAY_WAKE_EVENT_DOCKON); + pr_info("%s: dd->chargerstatus is %d\n", + __func__, dd->chargerstatus); + break; + } + + return NOTIFY_OK; +} +#endif + static void m4als_work_func(struct work_struct *work) { int err = 0; @@ -81,8 +116,18 @@ static void m4als_work_func(struct work_struct *work) dd->luminosity = luminosity; - input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); - input_sync(dd->indev); +#ifdef CONFIG_ALS_WHILE_CHARGING + if (dd->chargerstatus == true) { + als_notify_subscriber(luminosity); + } else { + input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); + input_sync(dd->indev); + } +#else + input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); + input_sync(dd->indev); +#endif + if (dd->samplerate > 0) queue_delayed_work(system_freezable_wq, &(dd->m4als_work), msecs_to_jiffies(dd->samplerate)); @@ -101,6 +146,12 @@ static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate) int err = 0; int size = 0; +#ifdef CONFIG_ALS_WHILE_CHARGING + if (rate == -1 && dd->chargerstatus == true) { + rate = ALS_SAMPLERATE_WHILE_DOCKED * 1000; + } +#endif + if ((rate >= 0) && (rate <= dd->fastest_rate)) rate = dd->fastest_rate; @@ -132,9 +183,17 @@ static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate) } cancel_delayed_work(&(dd->m4als_work)); dd->samplerate = rate; - if (dd->samplerate > 0) + if (dd->samplerate > 0) { queue_delayed_work(system_freezable_wq, &(dd->m4als_work), msecs_to_jiffies(rate)); +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY + als_notify_subscriber(ALS_ENABLED); +#endif + } else { +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY + als_notify_subscriber(ALS_DISABLED); +#endif + } m4als_set_samplerate_fail: return err; @@ -153,6 +212,8 @@ static ssize_t m4als_setrate_store(struct device *dev, int err = 0; struct m4als_driver_data *dd = dev_get_drvdata(dev); int value = 0; + int regsize = 0; + uint16_t luminosity = 0; mutex_lock(&(dd->mutex)); @@ -175,6 +236,32 @@ static ssize_t m4als_setrate_store(struct device *dev, goto m4als_enable_store_exit; } + /* Read and send raw value for gesture wakeup */ + msleep(120); + regsize = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL); + if (regsize < 0) { + m4als_err("%s: Reading from invalid register %d.\n", + __func__, regsize); + err = regsize; + goto m4als_enable_store_exit; + } + + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL, + (char *)&luminosity); + if (err < 0) { + m4als_err("%s: Failed to read luminosity data.\n", __func__); + goto m4als_enable_store_exit; + } else if (err != regsize) { + m4als_err("%s: Read %d bytes instead of %d.\n", + __func__, err, regsize); + goto m4als_enable_store_exit; + } + + dd->luminosity = luminosity; + + input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); + input_sync(dd->indev); + m4als_enable_store_exit: if (err < 0) m4als_err("%s: Failed with error code %d.\n", __func__, err); @@ -304,12 +391,6 @@ static int m4als_driver_init(struct init_calldata *p_arg) goto m4als_driver_init_fail; } - err = m4als_create_sysfs(dd); - if (err < 0) { - m4als_err("%s: Failed to create sysfs.\n", __func__); - goto m4als_driver_init_sysfs_fail; - } - INIT_DELAYED_WORK(&(dd->m4als_work), m4als_work_func); err = m4sensorhub_panic_register(dd->m4, PANICHDL_ALS_RESTORE, @@ -318,8 +399,6 @@ static int m4als_driver_init(struct init_calldata *p_arg) KDEBUG(M4SH_ERROR, "Als panic callback register failed\n"); goto m4als_driver_init_exit; -m4als_driver_init_sysfs_fail: - input_unregister_device(dd->indev); m4als_driver_init_fail: m4als_err("%s: Init failed with error code %d.\n", __func__, err); m4als_driver_init_exit: @@ -359,8 +438,22 @@ static int m4als_probe(struct platform_device *pdev) goto m4als_probe_fail; } + err = m4als_create_sysfs(dd); + if (err < 0) { + m4als_err("%s: Failed to create sysfs.\n", __func__); + goto m4als_driver_init_sysfs_fail; + } + +#ifdef CONFIG_ALS_WHILE_CHARGING + dd->chargerstatus = false; + dd->charger_nb.notifier_call = charger_notify; + wakeup_source_register_notify(&dd->charger_nb); +#endif + return 0; +m4als_driver_init_sysfs_fail: + m4sensorhub_unregister_initcall(m4als_driver_init); m4als_probe_fail: mutex_destroy(&(dd->mutex)); kfree(dd); @@ -377,6 +470,9 @@ static int __exit m4als_remove(struct platform_device *pdev) cancel_delayed_work(&(dd->m4als_work)); m4als_remove_sysfs(dd); m4sensorhub_unregister_initcall(m4als_driver_init); +#ifdef CONFIG_ALS_WHILE_CHARGING + wakeup_source_unregister_notify(&dd->charger_nb); +#endif if (dd->indev != NULL) input_unregister_device(dd->indev); mutex_destroy(&(dd->mutex)); diff --git a/drivers/misc/m4sensorhub_fusion.c b/drivers/misc/m4sensorhub_fusion.c index e1330f56263..816b89bea39 100644 --- a/drivers/misc/m4sensorhub_fusion.c +++ b/drivers/misc/m4sensorhub_fusion.c @@ -126,6 +126,90 @@ static void m4fus_work_func(struct work_struct *work) dd->iiodat[1].type = FUSION_TYPE_ORIENTATION; dd->iiodat[1].timestamp = iio_get_time_ns(); + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYX); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYX, + (char *)&(dd->iiodat[2].values[0])); + if (err < 0) { + m4fus_err("%s: Failed to read gravityX data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "gravityX"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYY); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYY, + (char *)&(dd->iiodat[2].values[1])); + if (err < 0) { + m4fus_err("%s: Failed to read gravityY data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "gravityY"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYZ); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYZ, + (char *)&(dd->iiodat[2].values[2])); + if (err < 0) { + m4fus_err("%s: Failed to read gravityZ data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "gravityZ"); + err = -EBADE; + goto m4fus_isr_fail; + } + + dd->iiodat[2].type = FUSION_TYPE_GRAVITY; + dd->iiodat[2].timestamp = iio_get_time_ns(); + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALX); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALX, + (char *)&(dd->iiodat[3].values[0])); + if (err < 0) { + m4fus_err("%s: Failed to read localX data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "localX"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALY); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALY, + (char *)&(dd->iiodat[3].values[1])); + if (err < 0) { + m4fus_err("%s: Failed to read localY data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "localY"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALZ); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALZ, + (char *)&(dd->iiodat[3].values[2])); + if (err < 0) { + m4fus_err("%s: Failed to read localZ data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "localZ"); + err = -EBADE; + goto m4fus_isr_fail; + } + + dd->iiodat[3].type = FUSION_TYPE_LINEAR_ACCELERATION; + dd->iiodat[3].timestamp = iio_get_time_ns(); + /* * For some reason, IIO knows we are sending an array, * so all FUSION_TYPE_* indicies will be sent diff --git a/drivers/misc/m4sensorhub_gesture.c b/drivers/misc/m4sensorhub_gesture.c index d5e364ea6c5..9ccbbe3091e 100644 --- a/drivers/misc/m4sensorhub_gesture.c +++ b/drivers/misc/m4sensorhub_gesture.c @@ -114,7 +114,7 @@ static void m4ges_isr(enum m4sensorhub_irqs int_event, void *handle) /* the GESTURE_VIEW is only effect for kernel now * do not send gesture to android */ - goto m4ges_isr_fail; + goto m4ges_no_iio_push; } #endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ @@ -122,6 +122,12 @@ static void m4ges_isr(enum m4sensorhub_irqs int_event, void *handle) iio_push_to_buffers(iio, (unsigned char *)&(dd->iiodat)); dd->gesture_count++; +m4ges_no_iio_push: + /* Log gestures received */ + pr_info("%s: Gesture received: count=%u, type=%hhu, value=%hhd.\n", + __func__, dd->gesture_count, dd->iiodat.gesture_type, + dd->iiodat.gesture_value); + m4ges_isr_fail: if (err < 0) m4ges_err("%s: Failed with error code %d.\n", __func__, err); diff --git a/drivers/misc/m4sensorhub_mpu9150.c b/drivers/misc/m4sensorhub_mpu9150.c index a2411c27d4f..55d160bc607 100644 --- a/drivers/misc/m4sensorhub_mpu9150.c +++ b/drivers/misc/m4sensorhub_mpu9150.c @@ -79,8 +79,6 @@ struct mpu9150_client { struct mutex mutex; /* prevent concurrent thread access */ struct delayed_work mpu9150_work[NUM_TYPES]; signed short fastest_rate[NUM_TYPES]; - int calibration_done; - int app_override; }; struct mpu9150_client *misc_mpu9150_data; @@ -142,23 +140,6 @@ static void m4_report_mpu9150_inputevent( } } -static void m4_queue_delayed_work(struct mpu9150_client *dd, - int delay, enum mpu9150_sensor type) -{ - if (type == TYPE_COMPASS) { - /* initial calibration is not done and there is - no app requesting compass data */ - if ((!dd->calibration_done) && (!dd->app_override)) - /* For current drain saving, m4 is sampling - compass at 40ms while omap is polling - for compass data at 15 secs */ - delay = 15000; - } - queue_delayed_work(system_freezable_wq, - &(dd->mpu9150_work[type]), - msecs_to_jiffies(delay)); -} - static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data, int delay, enum mpu9150_sensor type) { @@ -197,18 +178,19 @@ static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data, cancel_delayed_work(&(dd->mpu9150_work[type])); dd->samplerate[type] = delay; if (dd->samplerate[type] > 0) - m4_queue_delayed_work(dd, delay, type); + queue_delayed_work(system_freezable_wq, + &(dd->mpu9150_work[type]), + msecs_to_jiffies(delay)); + } } - static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data, enum mpu9150_sensor type) { sCompassData compassdata; sAccelData acceldata; sGyroData gyrodata; - struct mpu9150_client *dd = mpu9150_client_data; switch (type) { case TYPE_GYRO: @@ -249,18 +231,6 @@ static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data, mpu9150_client_data->compass_data.cz = compassdata.z; mpu9150_client_data->compass_data.ca = compassdata.accuracy; - /* Check if calibration is complete */ - if ((!(dd->calibration_done)) && (compassdata.accuracy)) { - dd->calibration_done = 1; - KDEBUG(M4SH_INFO, "Calibration complete\n"); - /* Stop compass sampling if no app is using the data */ - if (dd->app_override == 0) { - m4_set_mpu9150_delay(dd, - -1, - TYPE_COMPASS); - KDEBUG(M4SH_INFO, "Init cal done. Turning off compass"); - } - } break; default: @@ -320,7 +290,9 @@ static void m4compass_work_func(struct work_struct *work) m4_report_mpu9150_inputevent(dd, TYPE_COMPASS); rate = dd->samplerate[TYPE_COMPASS]; if (rate > 0) - m4_queue_delayed_work(dd, rate, TYPE_COMPASS); + queue_delayed_work(system_freezable_wq, + &(dd->mpu9150_work[TYPE_COMPASS]), + msecs_to_jiffies(rate)); mutex_unlock(&(dd->mutex)); } @@ -407,20 +379,8 @@ static ssize_t m4_mpu9150_write_compass_setdelay(struct device *dev, mutex_lock(&(misc_mpu9150_data->mutex)); - if (misc_mpu9150_data->calibration_done == 0) { - /* If calibration is not complete and app tries to - turn off ignore */ - if (scanresult < 0) { - misc_mpu9150_data->app_override = 0; - goto compass_setdelay_exit; - } else { - misc_mpu9150_data->app_override = 1; - } - } m4_set_mpu9150_delay(misc_mpu9150_data, scanresult, TYPE_COMPASS); -compass_setdelay_exit: - mutex_unlock(&(misc_mpu9150_data->mutex)); return count; @@ -611,7 +571,9 @@ static void mpu9150_panic_restore(struct m4sensorhub_data *m4sensorhub, m4_set_mpu9150_delay(dd, rate, type); cancel_delayed_work(&(dd->mpu9150_work[type])); if (rate > 0) - m4_queue_delayed_work(dd, rate, type); + queue_delayed_work(system_freezable_wq, + &(dd->mpu9150_work[type]), + msecs_to_jiffies(rate)); } mutex_unlock(&(dd->mutex)); } @@ -633,9 +595,7 @@ static int mpu9150_driver_init(struct init_calldata *p_arg) dd); if (ret < 0) KDEBUG(M4SH_ERROR, "HR panic callback register failed\n"); - m4_set_mpu9150_delay(dd, - 40, - TYPE_COMPASS); + mutex_unlock(&(dd->mutex)); return ret; } @@ -668,8 +628,6 @@ static int mpu9150_client_probe(struct platform_device *pdev) mpu9150_client_data->fastest_rate[TYPE_ACCEL] = 40; mpu9150_client_data->fastest_rate[TYPE_GYRO] = 40; mpu9150_client_data->fastest_rate[TYPE_COMPASS] = 40; - mpu9150_client_data->calibration_done = 0; - mpu9150_client_data->app_override = 0; mpu9150_client_data->input_dev = input_allocate_device(); if (!mpu9150_client_data->input_dev) { diff --git a/drivers/misc/m4sensorhub_pedometer.c b/drivers/misc/m4sensorhub_pedometer.c index cf326454bea..0be14698e1d 100644 --- a/drivers/misc/m4sensorhub_pedometer.c +++ b/drivers/misc/m4sensorhub_pedometer.c @@ -40,6 +40,8 @@ #define M4PED_IRQ_ENABLED_BIT 0 #define M4PED_FEATURE_ENABLED_BIT 1 +#define M4PED_USERDATA_SIZE 5 + struct m4ped_driver_data { struct platform_device *pdev; struct m4sensorhub_data *m4; @@ -47,9 +49,12 @@ struct m4ped_driver_data { struct m4sensorhub_pedometer_iio_data iiodat; struct m4sensorhub_pedometer_iio_data base_dat; - struct delayed_work m4ped_work; + struct m4sensorhub_pedometer_iio_data last_dat; + struct delayed_work m4ped_work; + int16_t samplerate; int16_t fastest_rate; + uint8_t userdata[M4PED_USERDATA_SIZE]; uint16_t status; }; @@ -57,6 +62,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, struct m4ped_driver_data *dd) { int err = 0, size = 0; + struct m4sensorhub_pedometer_iio_data dat; /*input validations */ if ((iio == NULL) || (dd == NULL)) { @@ -80,7 +86,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_PEDOMETER_TOTALDISTANCE); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_PEDOMETER_TOTALDISTANCE, - (char *)&(dd->iiodat.total_distance)); + (char *)&(dat.total_distance)); if (err < 0) { m4ped_err("%s: Failed to read total_distance data.\n", __func__); @@ -94,7 +100,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_PEDOMETER_TOTALSTEPS); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_PEDOMETER_TOTALSTEPS, - (char *)&(dd->iiodat.total_steps)); + (char *)&(dat.total_steps)); if (err < 0) { m4ped_err("%s: Failed to read total_steps data.\n", __func__); goto m4ped_read_fail; @@ -121,7 +127,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_HEALTHYMINUTES); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_HEALTHYMINUTES, - (char *)&(dd->iiodat.healthy_minutes)); + (char *)&(dat.healthy_minutes)); if (err < 0) { m4ped_err("%s: Failed to read healthy_minutes data.\n", __func__); @@ -135,7 +141,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_CALORIES); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_CALORIES, - (char *)&(dd->iiodat.calories)); + (char *)&(dat.calories)); if (err < 0) { m4ped_err("%s: Failed to read calories data.\n", __func__); goto m4ped_read_fail; @@ -146,12 +152,63 @@ static int m4ped_read_report_data(struct iio_dev *iio, goto m4ped_read_fail; } + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_CALORIES_NO_RMR); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_CALORIES_NO_RMR, + (char *)&(dat.calories_normr)); + if (err < 0) { + m4ped_err("%s: Failed to read calories_normr data.\n", + __func__); + goto m4ped_read_fail; + } else if (err != size) { + m4ped_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "calories_normr"); + err = -EBADE; + goto m4ped_read_fail; + } + dd->iiodat.timestamp = iio_get_time_ns(); - dd->iiodat.total_distance += dd->base_dat.total_distance; - dd->iiodat.total_steps += dd->base_dat.total_steps; - dd->iiodat.healthy_minutes += dd->base_dat.healthy_minutes; - dd->iiodat.calories += dd->base_dat.calories; + /* Save data if these values decrease (they monotonically increase) */ + if ((dat.total_distance < dd->last_dat.total_distance) || + (dat.total_steps < dd->last_dat.total_steps) || + (dat.healthy_minutes < dd->last_dat.healthy_minutes) || + (dat.calories < dd->last_dat.calories) || + (dat.calories_normr < dd->last_dat.calories_normr)) { + m4ped_err("%s: Error: Current = %u %u %u %u %u " + "Last = %u %u %u %u %u, Base = %u %u %u %u %u\n", + __func__, dat.total_distance, + dat.total_steps, dat.healthy_minutes, + dat.calories, dat.calories_normr, + dd->last_dat.total_distance, + dd->last_dat.total_steps, + dd->last_dat.healthy_minutes, dd->last_dat.calories, + dd->last_dat.calories_normr, + dd->base_dat.total_distance, + dd->base_dat.total_steps, + dd->base_dat.healthy_minutes, dd->base_dat.calories, + dd->base_dat.calories_normr); + m4ped_err("%s: iio = %u %u %u %u %u\n", __func__, + dd->iiodat.total_distance, + dd->iiodat.total_steps, + dd->iiodat.healthy_minutes, dd->iiodat.calories, + dd->iiodat.calories_normr); + goto m4ped_read_fail; + } + + dd->last_dat.total_distance = dat.total_distance; + dd->last_dat.total_steps = dat.total_steps; + dd->last_dat.healthy_minutes = dat.healthy_minutes; + dd->last_dat.calories = dat.calories; + dd->last_dat.calories_normr = dat.calories_normr; + + dd->iiodat.total_distance = dat.total_distance + + dd->base_dat.total_distance; + dd->iiodat.total_steps = dat.total_steps + dd->base_dat.total_steps; + dd->iiodat.healthy_minutes = dat.healthy_minutes + + dd->base_dat.healthy_minutes; + dd->iiodat.calories = dat.calories + dd->base_dat.calories; + dd->iiodat.calories_normr = dat.calories_normr + + dd->base_dat.calories_normr; iio_push_to_buffers(iio, (unsigned char *)&(dd->iiodat)); @@ -181,6 +238,42 @@ static void m4ped_work_func(struct work_struct *work) return; } +static int m4ped_write_userdata(struct m4ped_driver_data *dd) +{ + int err; + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, + &(dd->userdata[0]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write age.\n", __func__); + goto m4ped_write_userdata_fail; + } + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERGENDER, + &(dd->userdata[1]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write gender.\n", __func__); + goto m4ped_write_userdata_fail; + } + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERHEIGHT, + &(dd->userdata[2]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write height.\n", __func__); + goto m4ped_write_userdata_fail; + } + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERWEIGHT, + &(dd->userdata[3]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write weight.\n", __func__); + goto m4ped_write_userdata_fail; + } + +m4ped_write_userdata_fail: + return err; +} + static int m4ped_set_samplerate(struct iio_dev *iio, int16_t rate) { int err = 0; @@ -278,13 +371,14 @@ static ssize_t m4ped_iiodata_show(struct device *dev, mutex_lock(&(dd->mutex)); size = snprintf(buf, PAGE_SIZE, - "%s%hhu\n%s%u\n%s%u\n%s%hu\n%s%u\n%s%u\n", + "%s%hhu\n%s%u\n%s%u\n%s%hu\n%s%u\n%s%u\n%s%u\n", "ped_activity: ", dd->iiodat.ped_activity, "total_distance: ", dd->iiodat.total_distance, "total_steps: ", dd->iiodat.total_steps, "current_speed: ", dd->iiodat.current_speed, "healthy_minutes: ", dd->iiodat.healthy_minutes, - "calories: ", dd->iiodat.calories); + "calories: ", dd->iiodat.calories, + "calories_normr: ", dd->iiodat.calories_normr); mutex_unlock(&(dd->mutex)); return size; } @@ -298,11 +392,11 @@ static ssize_t m4ped_userdata_show(struct device *dev, struct iio_dev *iio = platform_get_drvdata(pdev); struct m4ped_driver_data *dd = iio_priv(iio); ssize_t size = 0; - uint8_t data[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t data[M4PED_USERDATA_SIZE] = {0x00}; mutex_lock(&(dd->mutex)); - err = m4sensorhub_reg_read_n(dd->m4, M4SH_REG_USERSETTINGS_SCREENSTATUS, + err = m4sensorhub_reg_read_n(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, (char *)&(data[0]), ARRAY_SIZE(data)); if (err < 0) { m4ped_err("%s: Failed to read user data.\n", __func__); @@ -315,11 +409,11 @@ static ssize_t m4ped_userdata_show(struct device *dev, } size = snprintf(buf, PAGE_SIZE, - "%s%s\n%s%hhu\n%s%hhu\n%s%hhu\n", - "Gender (M/F): ", data[2] ? "M" : "F", - "Age (yrs): ", data[1], - "Height (cm): ", data[3], - "Weight (kg): ", data[4]); + "%s%s\n%s%hhu\n%s%hhu\n%s%hu\n", + "Gender (M/F): ", data[1] ? "M" : "F", + "Age (yrs): ", data[0], + "Height (cm): ", data[2], + "Weight (kg): ", data[3] | (data[4] << 8)); m4ped_userdata_show_fail: mutex_unlock(&(dd->mutex)); @@ -334,6 +428,7 @@ m4ped_userdata_show_fail: * Example: * Female, 22, 168cm, 49kg * 0x00,0x16,0xA7,0x31\n + * echo 0x00,0x16,0xA7,0x31 > userdata */ static ssize_t m4ped_userdata_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -344,7 +439,7 @@ static ssize_t m4ped_userdata_store(struct device *dev, struct m4ped_driver_data *dd = iio_priv(iio); unsigned int value = 0; unsigned char convbuf[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; - unsigned char outbuf[4] = {0x00, 0x00, 0x00, 0x00}; + unsigned char outbuf[M4PED_USERDATA_SIZE] = {0x00}; int i = 0; mutex_lock(&(dd->mutex)); @@ -373,31 +468,16 @@ static ssize_t m4ped_userdata_store(struct device *dev, outbuf[i] = (unsigned char) value; } - err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, - &(outbuf[1]), m4sh_no_mask); - if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); - goto m4ped_userdata_store_fail; - } + for (i = 0; i < M4PED_USERDATA_SIZE; i++) + dd->userdata[i] = outbuf[i]; - err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERGENDER, - &(outbuf[0]), m4sh_no_mask); - if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); - goto m4ped_userdata_store_fail; - } - - err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERHEIGHT, - &(outbuf[2]), m4sh_no_mask); - if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); - goto m4ped_userdata_store_fail; - } + dd->userdata[0] = outbuf[1]; /* Age */ + dd->userdata[1] = outbuf[0]; /* Gender */ - err = m4sensorhub_reg_write_n(dd->m4, M4SH_REG_USERSETTINGS_USERWEIGHT, - &(outbuf[3]), m4sh_no_mask, 1); + err = m4ped_write_userdata(dd); if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); + m4ped_err("%s: Failed to write user data (%d).\n", + __func__, err); goto m4ped_userdata_store_fail; } @@ -606,13 +686,12 @@ static void m4ped_panic_restore(struct m4sensorhub_data *m4sensorhub, mutex_lock(&(dd->mutex)); - dd->base_dat.total_distance = dd->iiodat.total_distance; - dd->base_dat.total_steps = dd->iiodat.total_steps; - dd->base_dat.healthy_minutes = dd->iiodat.healthy_minutes; - dd->base_dat.calories = dd->iiodat.calories; - m4ped_err("%s: Pedometer bases after panic = %d %d %d %d", __func__, - dd->base_dat.total_distance, dd->base_dat.total_steps, - dd->base_dat.healthy_minutes, dd->base_dat.calories); + err = m4ped_write_userdata(dd); + if (err < 0) { + m4ped_err("%s: Failed to write user data (%d).\n", + __func__, err); + goto m4ped_panic_restore_fail; + } if (!(dd->status & (1 << M4PED_FEATURE_ENABLED_BIT))) { err = m4sensorhub_reg_write(dd->m4, M4SH_REG_PEDOMETER_ENABLE, @@ -631,6 +710,19 @@ static void m4ped_panic_restore(struct m4sensorhub_data *m4sensorhub, goto m4ped_panic_restore_fail; } } + /* Update base and reset last */ + dd->base_dat.total_distance = dd->iiodat.total_distance; + dd->base_dat.total_steps = dd->iiodat.total_steps; + dd->base_dat.healthy_minutes = dd->iiodat.healthy_minutes; + dd->base_dat.calories = dd->iiodat.calories; + dd->base_dat.calories_normr = dd->iiodat.calories_normr; + + dd->last_dat.total_distance = 0; + dd->last_dat.total_steps = 0; + dd->last_dat.healthy_minutes = 0; + dd->last_dat.calories = 0; + dd->last_dat.calories_normr = 0; + cancel_delayed_work(&(dd->m4ped_work)); if (dd->samplerate > 0) queue_delayed_work(system_freezable_wq, &(dd->m4ped_work), @@ -692,6 +784,12 @@ static int m4ped_probe(struct platform_device *pdev) dd->fastest_rate = 1000; /* in milli secs */ dd->status = dd->status | (1 << M4PED_FEATURE_ENABLED_BIT); + dd->userdata[0] = 0x23; /* Age (35) */ + dd->userdata[1] = 0x01; /* Gender (Male) */ + dd->userdata[2] = 0xB2; /* Height (178cm) */ + dd->userdata[3] = 0x5B; /* Weight (91kg) */ + dd->userdata[4] = 0x00; /* Weight */ + err = m4ped_create_iiodev(iio); /* iio and dd are freed on fail */ if (err < 0) { m4ped_err("%s: Failed to create IIO device.\n", __func__); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d04f741899..60d5c8e01cb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2311,6 +2311,8 @@ static const struct mmc_fixup blk_fixups[] = MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("4FEACB", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), MMC_FIXUP("004G90", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile index 9827e6ce4ec..7b8ef7ee817 100644 --- a/drivers/power/avs/Makefile +++ b/drivers/power/avs/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),) # OMAP Common -omap-volt-common = omap_vc.o omap_vp.o +omap-volt-common = omap_vc.o omap_vp.o omap_core_dvfs.o # OMAP SoC specific ifneq ($(CONFIG_ARCH_OMAP3),) diff --git a/drivers/power/avs/omap_core_dvfs.c b/drivers/power/avs/omap_core_dvfs.c new file mode 100644 index 00000000000..2f98d534fbb --- /dev/null +++ b/drivers/power/avs/omap_core_dvfs.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/err.h> + +#define DRIVER_NAME "omap-core-dvfs" + + +struct omap_core_dvfs_map { + unsigned long cpu_freq; + unsigned long core_freq; +}; + +struct omap_core_dvfs_data { + struct device *dev; + struct clk *l3_clock; + struct clk *dpll_clock; + struct regulator *reg; + unsigned int volt_tolerance; + long curr_freq; + struct omap_core_dvfs_map *map; +}; + +static struct omap_core_dvfs_data *core_dvfs_data; + +static const struct of_device_id omap_core_dvfs_match_tbl[] = { + {.compatible = "ti,omap-core-dvfs"}, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_core_dvfs_match_tbl); +static int get_match_freq(unsigned long cpu_freq, unsigned long *core_freq) +{ + struct omap_core_dvfs_map *map = core_dvfs_data->map; + while (map->core_freq && map->cpu_freq) { + if (map->cpu_freq == cpu_freq) { + *core_freq = map->core_freq; + return 0; + } + map++; + } + return -ENODATA; +} +static int cpufreq_trans(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + struct omap_core_dvfs_data *pdata = core_dvfs_data; + int ret = 0; + long d, new_freq, dpll_freq, old_freq; + struct opp *opp; + unsigned long volt = 0, volt_old = 0, tol = 0; + + if (IS_ERR_OR_NULL(freqs)) + return -ENODATA; + + if (val != CPUFREQ_PRECHANGE || freqs->new == freqs->old) + return 0; + + ret = get_match_freq(freqs->new * 1000, &new_freq); + if (ret) { + pr_err( + "Could not find cpu freq %u in core map\n", freqs->new * 1000); + goto f_out; + } + if (pdata->curr_freq == new_freq) + return 0; + + /* calculate target frequency to be set in dpll */ + old_freq = clk_get_rate(pdata->l3_clock); + d = clk_get_rate(pdata->dpll_clock) / old_freq; + dpll_freq = clk_round_rate(pdata->dpll_clock, new_freq * d); + if (dpll_freq < 0) + dpll_freq = new_freq * d; + + rcu_read_lock(); + opp = opp_find_freq_ceil(pdata->dev, &new_freq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + pr_err("failed to find core OPP for %ld\n", new_freq); + ret = PTR_ERR(opp); + goto f_out; + } + volt = opp_get_voltage(opp); + rcu_read_unlock(); + + tol = volt * pdata->volt_tolerance / 100; + volt_old = regulator_get_voltage(pdata->reg); + + pr_debug("L3 DVFS %ld MHz, %ld mV --> %ld MHz, %ld mV\n", + old_freq / 1000000, volt_old ? volt_old / 1000 : -1, + dpll_freq / d / 1000000, volt ? volt / 1000 : -1); + + if (freqs->new > freqs->old) { + ret = regulator_set_voltage_tol(pdata->reg, volt, tol); + if (ret) { + pr_err("failed to scale core voltage up: %d\n", ret); + goto f_out; + } + } + ret = clk_set_rate(pdata->dpll_clock, dpll_freq); + if (ret) { + pr_err("failed to set core clock rate: %d\n", ret); + regulator_set_voltage_tol(pdata->reg, volt_old, tol); + goto f_out; + } + if (freqs->new < freqs->old) { + ret = regulator_set_voltage_tol(pdata->reg, volt, tol); + if (ret) { + pr_err("failed to scale voltage down: %d\n", ret); + clk_set_rate(pdata->dpll_clock, old_freq * d); + goto f_out; + } + } + pdata->curr_freq = new_freq; +f_out: + return ret; +} + +static struct notifier_block cpufreq_trans_block = { + .notifier_call = cpufreq_trans +}; + +static int of_init_opp_map(struct device *dev, struct omap_core_dvfs_map **map) +{ + const struct property *prop; + const __be32 *val; + struct omap_core_dvfs_map *m; + int nr, i; + + prop = of_find_property(dev->of_node, "map", NULL); + if (!prop) + return -ENODEV; + if (!prop->value) + return -ENODATA; + + /* + * Each entry is a set of tuples consisting of freq-kHz from + * OPP CPU list and index of matching OPP in core list. + */ + nr = prop->length / sizeof(u32); + if (nr % 2) { + dev_err(dev, "Invalid map\n"); + return -EINVAL; + } + + m = devm_kzalloc(dev, prop->length, GFP_KERNEL); + if (!m) { + dev_err(dev, "Unable to create new map\n"); + return -ENOMEM; + } + *map = m; + val = prop->value; + for (i = 0; i < nr; i += 2, m++) { + m->cpu_freq = be32_to_cpup(val++) * 1000; + m->core_freq = be32_to_cpup(val++) * 1000; + } + m->cpu_freq = 0; + m->core_freq = 0; + + return 0; +} + +static int omap_core_dvfs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *nd = dev->of_node; + int ret; + struct omap_core_dvfs_data *data; + const char *pname, *str; + struct regulator *reg; + + if (!nd) { + dev_err(dev, "no OF information?\n"); + return -EINVAL; + } + + reg = devm_regulator_get(dev, "core_dvfs"); + if (IS_ERR(reg)) { + dev_err(dev, "core_dvfs regulator not ready, retry\n"); + return -EPROBE_DEFER; + } + + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(dev, "Unable to allocate data\n"); + return -ENOMEM; + } + + ret = of_init_opp_table(dev); + if (ret) { + dev_err(dev, "Failed to init OPP table: %d\n", ret); + goto fail; + } + + ret = of_init_opp_map(dev, &data->map); + if (ret) { + dev_err(dev, "Failed to init map: %d\n", ret); + goto fail; + } + pname = "l3_clkname"; + ret = of_property_read_string(nd, pname, &str); + if (ret) + goto property_err; + + data->l3_clock = devm_clk_get(dev, str); + if (IS_ERR(data->l3_clock)) { + ret = PTR_ERR(data->l3_clock); + dev_err(dev, "Failed to get %s clock: %d\n", str, ret); + goto fail; + } + + pname = "dpll_clkname"; + ret = of_property_read_string(nd, pname, &str); + if (ret) + goto property_err; + + data->dpll_clock = devm_clk_get(dev, str); + if (IS_ERR(data->dpll_clock)) { + ret = PTR_ERR(data->dpll_clock); + dev_err(dev, "Failed to get %s clock: %d\n", str, ret); + goto fail; + } + + of_property_read_u32(nd, "voltage-tolerance", &data->volt_tolerance); + + ret = cpufreq_register_notifier(&cpufreq_trans_block, + CPUFREQ_TRANSITION_NOTIFIER); + if (ret) { + dev_err(dev, "CPU notifier registration failed with %d\n", ret); + cpufreq_unregister_notifier( + &cpufreq_trans_block, CPUFREQ_TRANSITION_NOTIFIER); + goto fail; + } + data->reg = reg; + data->dev = dev; + core_dvfs_data = data; + platform_set_drvdata(pdev, data); + + return 0; + +property_err: + dev_err(dev, " Missing/Invalid '%s' property\n", pname); + +fail: + return ret; +} + +static struct platform_driver omap_core_dvfs_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_core_dvfs_match_tbl), + }, + .probe = omap_core_dvfs_probe, +}; +static int __init omap_core_dvfs_init(void) +{ + int ret; + ret = platform_driver_register(&omap_core_dvfs_driver); + if (ret) + pr_err("driver register failed for omap_pmic(%d)\n", ret); + return ret; +} +device_initcall_sync(omap_core_dvfs_init); + +static void __exit omap_core_dvfs_exit(void) +{ + platform_driver_unregister(&omap_core_dvfs_driver); +} +module_exit(omap_core_dvfs_exit); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("OMAP Global PRM driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 6d0327f120a..c6c70c4afeb 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -64,15 +64,23 @@ CONFIG_TS_BIT_ENBL | CONFIG_SS_BIT_ENBL) #define MODEL_LOCK1 0X0000 #define MODEL_LOCK2 0X0000 -#define dQ_ACC_DIV 0x4 -#define dP_ACC_100 0x1900 -#define dP_ACC_200 0x3200 +#define MAX17042_INIT_NUM_CYCLES 160 +#define MAX17047_INIT_NUM_CYCLES 96 + +#define MAX17042_dQ_ACC_DIV 4 +#define MAX17047_dQ_ACC_DIV 16 + +#define MAX17042_dP_ACC_200 0x3200 +#define MAX17047_dP_ACC_200 0x0C80 #define MAX17042_IC_VERSION 0x0092 #define MAX17047_IC_VERSION 0x00AC /* same for max17050 */ +#define MAX17042_AGE_DIV 256 + #define INIT_DATA_PROPERTY "maxim,regs-init-data" #define CONFIG_NODE "maxim,configuration" +#define VERSION_PROPERTY "version" #define CONFIG_PROPERTY "config" #define FULL_SOC_THRESH_PROPERTY "full_soc_thresh" #define DESIGN_CAP_PROPERTY "design_cap" @@ -119,6 +127,26 @@ struct max17042_chip { int malicious_online; }; +#ifdef CONFIG_OF +const char *get_dts_batt_id(struct device *dev) +{ + int lenp; + const char *retval = NULL; + struct device_node *n = of_find_node_by_path("/chosen"); + + if (n) { + retval = of_get_property(n, "batt-id", &lenp); + if (!retval || !lenp) { + dev_err(dev, "%s: batt-id len %d\n", __func__, lenp); + retval = NULL; + } + of_node_put(n); + } + + return retval; +} +#endif + static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value) { int ret = i2c_smbus_write_word_data(client, reg, value); @@ -598,8 +626,9 @@ static void max17042_update_capacity_regs(struct max17042_chip *chip) max17042_write_verify_reg(chip->client, MAX17042_FullCAP, config->fullcap); + /* Set DesignCap to fullcapnom here */ max17042_write_reg(chip->client, MAX17042_DesignCap, - config->design_cap); + config->fullcapnom); max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom, config->fullcapnom); } @@ -614,10 +643,21 @@ static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK); } +static void max17042_advance_to_coulomb_counter_mode(struct max17042_chip *chip) +{ + u16 value = (chip->chip_type == MAX17042 ? + MAX17042_INIT_NUM_CYCLES : MAX17047_INIT_NUM_CYCLES); + max17042_write_verify_reg(chip->client, MAX17042_Cycles, value); +} + static void max17042_load_new_capacity_params(struct max17042_chip *chip) { u16 rep_cap, dq_acc, vfSoc; u32 rem_cap; + u16 dQ_ACC_DIV = (chip->chip_type == MAX17042 ? + MAX17042_dQ_ACC_DIV : MAX17047_dQ_ACC_DIV); + u16 dP_ACC_200 = (chip->chip_type == MAX17042 ? + MAX17042_dP_ACC_200 : MAX17047_dP_ACC_200); struct max17042_config_data *config = chip->pdata->config_data; @@ -669,25 +709,14 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) config->soc_alrt_thresh); max17042_override_por(client, MAX17042_CONFIG, config->config); max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer); - - max17042_override_por(client, MAX17042_DesignCap, config->design_cap); - max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term); - max17042_override_por(client, MAX17042_AtRate, config->at_rate); - max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg); - max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg); - max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg); max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg); max17042_override_por(client, MAX17042_MaskSOC, config->masksoc); - max17042_override_por(client, MAX17042_FullCAP, config->fullcap); - max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom); if (chip->chip_type == MAX17042) max17042_override_por(client, MAX17042_SOC_empty, config->socempty); max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty); - max17042_override_por(client, MAX17042_dQacc, config->dqacc); - max17042_override_por(client, MAX17042_dPacc, config->dpacc); if (chip->chip_type == MAX17042) max17042_override_por(client, MAX17042_V_empty, config->vempty); @@ -696,8 +725,6 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(client, MAX17042_TempNom, config->temp_nom); max17042_override_por(client, MAX17042_TempLim, config->temp_lim); max17042_override_por(client, MAX17042_FCTC, config->fctc); - max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0); - max17042_override_por(client, MAX17042_TempCo, config->tcompc0); if (chip->chip_type == MAX17042) { max17042_override_por(client, MAX17042_EmptyTempCo, config->empty_tempco); @@ -748,6 +775,9 @@ static int max17042_init_chip(struct max17042_chip *chip) /* reset vfsoc0 reg */ max17042_reset_vfsoc0_reg(chip); + /* advance to coulomb-counter mode */ + max17042_advance_to_coulomb_counter_mode(chip); + /* load new capacity params */ max17042_load_new_capacity_params(chip); @@ -821,8 +851,13 @@ static void max17042_init_worker(struct work_struct *work) ret = max17042_init_chip(chip); } - if (!ret) + if (!ret) { chip->init_complete = 1; + if (chip->chip_type == MAX17047) { + max17042_write_reg(chip->client, MAX17047_Config_Ver, + chip->pdata->config_data->version); + } + } mutex_unlock(&chip->lock); } @@ -831,11 +866,16 @@ static void max17042_malicious_removed_worker(struct work_struct *work) { struct max17042_chip *chip = container_of(work, struct max17042_chip, work_malicious_removed); + int ret; mutex_lock(&chip->lock); max17042_perform_soft_POR(chip); - max17042_init_chip(chip); + ret = max17042_init_chip(chip); + if (!ret && chip->chip_type == MAX17047) + max17042_write_reg(chip->client, MAX17047_Config_Ver, + chip->pdata->config_data->version); + dev_info(&chip->client->dev, "malicious ps removed, chip re-inited\n"); mutex_unlock(&chip->lock); @@ -949,6 +989,10 @@ static int max17042_cfg_rqrd_prop(struct device *dev, struct device_node *np, struct max17042_config_data *config_data) { + if (of_property_read_u16(np, VERSION_PROPERTY, + &config_data->version)) + return -EINVAL; + if (of_property_read_u16(np, CONFIG_PROPERTY, &config_data->config)) return -EINVAL; @@ -1008,13 +1052,26 @@ static void max17042_cfg_optnl_prop(struct device_node *np, static struct max17042_config_data * max17042_get_config_data(struct device *dev) { + char *config_node = NULL; + char config_node_path[64]; struct max17042_config_data *config_data; struct device_node *np = dev->of_node; if (!np) return NULL; - np = of_get_child_by_name(np, CONFIG_NODE); + config_node = (char *)get_dts_batt_id(dev); + if (config_node) { + snprintf(config_node_path, sizeof(config_node_path), + "%s-%s", CONFIG_NODE, config_node); + config_node = config_node_path; + } else { + config_node = CONFIG_NODE; + } + + dev_info(dev, "using %s profile\n", config_node); + + np = of_get_child_by_name(np, config_node); if (!np) return NULL; @@ -1175,6 +1232,23 @@ static int max17042_debugfs_write_data(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(data_fops, max17042_debugfs_read_data, max17042_debugfs_write_data, "0x%02llx\n"); +static int max17042_debugfs_read_capacity(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + *val = chip->debugfs_capacity; + return 0; +} + +static int max17042_debugfs_write_capacity(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + chip->debugfs_capacity = val; + power_supply_changed(&chip->battery); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(capacity_fops, max17042_debugfs_read_capacity, + max17042_debugfs_write_capacity, "%llu\n"); + static int max17042_debugfs_create(struct max17042_chip *chip) { chip->debugfs_root = debugfs_create_dir(dev_name(&chip->client->dev), @@ -1191,8 +1265,8 @@ static int max17042_debugfs_create(struct max17042_chip *chip) goto err_debugfs; chip->debugfs_capacity = 0xFF; - if (!debugfs_create_u8("capacity", S_IRUGO | S_IWUSR, - chip->debugfs_root, &chip->debugfs_capacity)) + if (!debugfs_create_file("capacity", S_IRUGO | S_IWUSR, + chip->debugfs_root, chip, &capacity_fops)) goto err_debugfs; return 0; @@ -1226,7 +1300,6 @@ static void max17042_external_power_changed(struct power_supply *psy) } } - static ssize_t max17042_show_alert_threshold(struct device *dev, struct device_attribute *attr, char *buf) @@ -1256,8 +1329,21 @@ static ssize_t max17042_store_alert_threshold(struct device *dev, static DEVICE_ATTR(alert_threshold, S_IRUGO | S_IWUSR, max17042_show_alert_threshold, max17042_store_alert_threshold); + +static ssize_t max17042_show_battery_age(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + int ret = max17042_read_reg(chip->client, MAX17042_Age); + + return ret < 0 ? ret : sprintf(buf, "%u\n", ret / MAX17042_AGE_DIV); +} +static DEVICE_ATTR(battery_age, S_IRUGO, max17042_show_battery_age, NULL); + static struct attribute *max17042_attrs[] = { &dev_attr_alert_threshold.attr, + &dev_attr_battery_age.attr, NULL, }; @@ -1265,6 +1351,20 @@ static struct attribute_group max17042_attr_group = { .attrs = max17042_attrs, }; +static bool max17042_new_config_data(struct max17042_chip *chip) +{ + int ret; + + if (chip->chip_type == MAX17042) + return false; + + ret = max17042_read_reg(chip->client, MAX17047_Config_Ver); + if (ret < 0) + return false; + + return (chip->pdata->config_data->version != ret); +} + static int max17042_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1384,7 +1484,7 @@ static int max17042_probe(struct i2c_client *client, } reg = max17042_read_reg(chip->client, MAX17042_STATUS); - if (reg & STATUS_POR_BIT) { + if (reg & STATUS_POR_BIT || max17042_new_config_data(chip)) { INIT_WORK(&chip->work, max17042_init_worker); schedule_work(&chip->work); } else { diff --git a/drivers/regulator/omap-pmic-regulator.c b/drivers/regulator/omap-pmic-regulator.c index a19f9ebb7de..2eab7c158e2 100644 --- a/drivers/regulator/omap-pmic-regulator.c +++ b/drivers/regulator/omap-pmic-regulator.c @@ -661,7 +661,22 @@ static struct platform_driver omap_pmic_driver = { }, .probe = omap_pmic_probe, }; -module_platform_driver(omap_pmic_driver); + +static int __init omap_pmic_init(void) +{ + int ret; + ret = platform_driver_register(&omap_pmic_driver); + if (ret) + pr_err("driver register failed for omap_pmic (%d)\n", ret); + return ret; +} +device_initcall_sync(omap_pmic_init); + +static void __exit omap_pmic_exit(void) +{ + platform_driver_unregister(&omap_pmic_driver); +} +module_exit(omap_pmic_exit); MODULE_DESCRIPTION("OMAP Generic PMIC Regulator"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-sensorhub.c b/drivers/rtc/rtc-sensorhub.c index f4e713d741f..16f95758f93 100644 --- a/drivers/rtc/rtc-sensorhub.c +++ b/drivers/rtc/rtc-sensorhub.c @@ -223,6 +223,9 @@ static int rtc_sensorhub_rtc_set_time(struct device *p_dev, "set time, but failed to set M4 clock!\n"); return -EIO; } + dev_dbg(p_dev, "Set RTC time to %d-%02d-%02d %02d:%02d:%02d UTC (%ld)\n", + p_tm->tm_year + 1900, p_tm->tm_mon + 1, p_tm->tm_mday, + p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, sec); return 0; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7d041f28cf2..3009a8909cc 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -172,6 +172,7 @@ struct uart_omap_port { struct pinctrl_state *pin_default; struct pinctrl_state *pin_idle; bool is_suspending; + spinlock_t delayed_rts_lock; /* protect need_delayed_rts*/ bool need_delayed_rts; bool in_transmit; int ext_rt_cnt; @@ -1372,12 +1373,16 @@ static int serial_omap_prepare(struct device *dev) static void serial_omap_complete(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&up->delayed_rts_lock, flags); if (up->need_delayed_rts && up->pin_default && up->pin_idle) { pinctrl_select_state(up->pins, up->pin_default); up->need_delayed_rts = 0; } up->is_suspending = false; + spin_unlock_irqrestore(&up->delayed_rts_lock, flags); } static int serial_omap_suspend(struct device *dev) @@ -1747,6 +1752,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up) static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); + unsigned long flags; /* * When using 'no_console_suspend', the console UART must not be @@ -1762,8 +1768,10 @@ static int serial_omap_runtime_suspend(struct device *dev) return -EINVAL; if (up->pin_idle) { + spin_lock_irqsave(&up->delayed_rts_lock, flags); pinctrl_select_state(up->pins, up->pin_idle); up->need_delayed_rts = 0; + spin_unlock_irqrestore(&up->delayed_rts_lock, flags); } up->context_loss_cnt = serial_omap_get_context_loss_count(up); diff --git a/drivers/video/omap2/displays/panel-minnow-common.h b/drivers/video/omap2/displays/panel-minnow-common.h index 1001cc67f46..13ce3050a9e 100644 --- a/drivers/video/omap2/displays/panel-minnow-common.h +++ b/drivers/video/omap2/displays/panel-minnow-common.h @@ -18,7 +18,7 @@ #ifndef _MINNOW_PANEL_COMMON_HEADER_ -#define INIT_DATA_VERSION (0x072314) /*MM/DD/YY*/ +#define INIT_DATA_VERSION (0x081214) /*MM/DD/YY*/ /* This header file is used to sync Bootloader and Kernel Display Initialize * Structure/Data, please make sure sync it for both Bootloader/Kernel when * it changes some settings for Solomon/Orise. Bootloader should pass @@ -215,8 +215,8 @@ static u8 panel_init_ssd2848_320x320[] = { 2, OTM3201_CMD, 0xE9, 0x46, /* Display Inversion Control (RB1h) */ 2, OTM3201_CMD, 0xB1, 0x12, -/* ??? undefined */ -2, OTM3201_CMD, 0xE2, 0xF0, +/* MIPI RX Delay Setting (RE2h) */ +2, OTM3201_CMD, 0xE2, 0xF5, /* Display Waveform Cycle setting (RBAh) */ 5, OTM3201_CMD, 0xBA, 0x06, 0x15, 0x2B, 0x01, /* RGB Interface Blanking Porch setting (RB3h) diff --git a/drivers/video/omap2/displays/panel-minnow.c b/drivers/video/omap2/displays/panel-minnow.c index 8d127ae401f..d1014b0af1d 100644 --- a/drivers/video/omap2/displays/panel-minnow.c +++ b/drivers/video/omap2/displays/panel-minnow.c @@ -302,6 +302,8 @@ struct minnow_panel_data { int id_panel; int x_offset; int y_offset; + int xres_um; + int yres_um; int reset_ms; int release_ms; @@ -439,6 +441,27 @@ static void minnow_panel_sync_resume_mlocked(struct minnow_panel_data *mpd) #endif #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +static char *action_to_str(unsigned long action) +{ + switch (action) { + case DISPLAY_WAKE_EVENT_POWERKEY: + return "power_key"; + case DISPLAY_WAKE_EVENT_TOUCH: + return "touch"; + case DISPLAY_WAKE_EVENT_GESTURE: + return "gesture_wrist"; + case DISPLAY_WAKE_EVENT_GESTURE_VIEWON: + return "gesture_view_on"; + case DISPLAY_WAKE_EVENT_GESTURE_VIEWOFF: + return "gesture_view_off"; + case DISPLAY_WAKE_EVENT_DOCKON: + return "dock_on"; + case DISPLAY_WAKE_EVENT_DOCKOFF: + return "dock_off"; + } + return "unsupported"; +} + static int omapdss_displayenable_notify(struct notifier_block *self, unsigned long action, void *dev) { @@ -448,7 +471,8 @@ static int omapdss_displayenable_notify(struct notifier_block *self, if (GET_WAKEUP_EVENT_TYPE(action) != WAKEUP_DISPLAY) return NOTIFY_OK; - dev_info(&mpd->dssdev->dev, "%s, action is %lu", __func__, action); + dev_info(&mpd->dssdev->dev, "%s, action is %lu-%s", + __func__, action, action_to_str(action)); switch (action) { case DISPLAY_WAKE_EVENT_POWERKEY: @@ -1420,6 +1444,14 @@ static void minnow_panel_get_resolution(struct omap_dss_device *dssdev, *yres = dssdev->panel.timings.y_res; } +static void minnow_panel_get_dimensions(struct omap_dss_device *dssdev, + u32 *xres, u32 *yres) +{ + struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); + *xres = mpd->xres_um; + *yres = mpd->yres_um; +} + static ssize_t minnow_panel_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1874,6 +1906,49 @@ static ssize_t minnow_panel_store_interactivemode(struct device *dev, return r ? r : count; } +static ssize_t minnow_panel_show_smartambient(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); + unsigned t; + + t = mpd->smart_ambient; + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static ssize_t minnow_panel_store_smartambient(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); + unsigned long t; + int r; + bool enable; + + r = kstrtoul(buf, 10, &t); + if (!r) { + mutex_lock(&mpd->lock); + enable = !!t; + if (mpd->state != DISPLAY_ENABLE) { + dev_err(&dssdev->dev, "%s failed as display is not enabled\n", + __func__); + r = -EBUSY; + } else if (mpd->smart_ambient != enable) { + mpd->smart_ambient = enable; + } + mutex_unlock(&mpd->lock); + if (r) + dev_err(&dssdev->dev, "setting smartambient_status to %ld failed %d\n", + t, r); + else + dev_dbg(&dssdev->dev, "setting smartambient_status to %ld succeeded\n", + t); + } + + return r ? r : count; +} static ssize_t minnow_panel_show_ambient_timeout(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2050,6 +2125,9 @@ static DEVICE_ATTR(init_data, S_IRUGO | S_IWUSR, static DEVICE_ATTR(interactivemode, S_IRUGO | S_IWUSR, minnow_panel_show_interactivemode, minnow_panel_store_interactivemode); +static DEVICE_ATTR(smartambient, S_IRUSR | S_IWUSR, + minnow_panel_show_smartambient, + minnow_panel_store_smartambient); static DEVICE_ATTR(ambient_timeout, S_IRUGO | S_IWUSR, minnow_panel_show_ambient_timeout, minnow_panel_store_ambient_timeout); @@ -2076,6 +2154,7 @@ static struct attribute *minnow_panel_attrs[] = { #endif #ifdef CONFIG_HAS_AMBIENTMODE &dev_attr_interactivemode.attr, + &dev_attr_smartambient.attr, &dev_attr_ambient_timeout.attr, #endif #ifdef PANEL_PERF_TIME @@ -2409,6 +2488,15 @@ static int minnow_panel_dt_init(struct minnow_panel_data *mpd) mpd->dsi_config.lp_clk_max); } + mpd->xres_um = 0; + mpd->yres_um = 0; + if (!of_property_read_u32_array(dt_node, "panel_size_um", range, 2)) { + mpd->xres_um = range[0]; + mpd->yres_um = range[1]; + DTINFO("physical panel width = %d um, height = %d um\n", + mpd->xres_um, mpd->yres_um); + } + return 0; } @@ -2453,6 +2541,7 @@ static int minnow_panel_probe(struct omap_dss_device *dssdev) mpd->dssdev = dssdev; mpd->first_enable = true; mpd->m4_state = DISPLAY_ENABLE; + mpd->interactive = true; r = minnow_panel_dt_init(mpd); if (r) @@ -3030,8 +3119,9 @@ static int minnow_panel_enable_mlocked(struct minnow_panel_data *mpd) bool update; int r = 0; - dev_info(&dssdev->dev, "%s: current state = %d\n", - __func__, dssdev->state); + dev_info(&dssdev->dev, "%s: current display is %s\n", __func__, + dssdev->state == OMAP_DSS_DISPLAY_DISABLED + ? "disabled" : "enabled"); if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) { wake_lock(&mpd->wake_lock); @@ -3067,8 +3157,9 @@ static void minnow_panel_disable_mlocked(struct minnow_panel_data *mpd) { struct omap_dss_device *dssdev = mpd->dssdev; - dev_info(&dssdev->dev, "%s: current state = %d\n", - __func__, dssdev->state); + dev_info(&dssdev->dev, "%s: current display is %s\n", __func__, + dssdev->state == OMAP_DSS_DISPLAY_DISABLED + ? "disabled" : "enabled"); wake_lock(&mpd->wake_lock); mpd->early_inited = false; @@ -3091,7 +3182,6 @@ static void minnow_panel_disable_mlocked(struct minnow_panel_data *mpd) static void minnow_panel_sync_display_status_mlocked( struct minnow_panel_data *mpd) { - struct m4sensorhub_data *m4sensorhub; enum display_state m4_state = mpd->state; /* special case for dock mode, set to DISPLAY_ENABLE * to block all wakeup gestures @@ -3104,21 +3194,19 @@ static void minnow_panel_sync_display_status_mlocked( /* be safety to sync resume states first */ minnow_panel_sync_resume_mlocked(mpd); - m4sensorhub = m4sensorhub_client_get_drvdata(); - if (m4sensorhub->mode != NORMALMODE) { + if (m4sensorhub_get_current_mode() != NORMALMODE) { dev_err(&mpd->dssdev->dev, "M4 is not ready, unable to set screen status(%d)\n", m4_state); return; } - if (m4sensorhub_reg_write_1byte(m4sensorhub, - M4SH_REG_USERSETTINGS_SCREENSTATUS, - m4_state, 0xFF) != 1) { + + if (m4sensorhub_extern_set_display_status(m4_state) < 0) { dev_err(&mpd->dssdev->dev, - "Unable to set screen status(%d) to M4\n", - m4_state); + "Unable to set screen status(%d) to M4\n", m4_state); return; } + dev_dbg(&mpd->dssdev->dev, "Set screen status(%d) to M4 success!\n", m4_state); mpd->m4_state = m4_state; @@ -3145,13 +3233,32 @@ static void led_set_dim_brightness(struct device *dev) } #endif /* CONFIG_HAS_AMBIENTMODE */ +static char *state_to_str(enum display_state state) +{ + switch (state) { + case DISPLAY_DISABLE: + return "normal_off"; + case DISPLAY_ENABLE: + return "normal_on"; +#ifdef CONFIG_HAS_AMBIENTMODE + case DISPLAY_AMBIENT_OFF: + return "ambient_off"; + case DISPLAY_AMBIENT_ON: + return "ambient_on"; +#endif + } + return "unknown???"; +} + static int minnow_panel_change_state_mlocked(struct minnow_panel_data *mpd, int state) { int r = 0; dev_info(&mpd->dssdev->dev, - "change state %d ==> %d\n", mpd->state, state); + "change state %d(%s) ==> %d(%s)\n", + mpd->state, state_to_str(mpd->state), + state, state_to_str(state)); /* already in state, return success */ if (state == mpd->state) { @@ -3364,7 +3471,7 @@ static void minnow_panel_te_timeout_work_callback(struct work_struct *work) static int minnow_panel_enable(struct omap_dss_device *dssdev) { struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); - int r; + int r, state; mutex_lock(&mpd->lock); #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY @@ -3374,7 +3481,12 @@ static int minnow_panel_enable(struct omap_dss_device *dssdev) cancel_delayed_work(&mpd->early_init_timeout_work); } #endif - r = minnow_panel_change_state_mlocked(mpd, DISPLAY_ENABLE); + state = DISPLAY_ENABLE; +#ifdef CONFIG_HAS_AMBIENTMODE + if (!mpd->interactive) + state = DISPLAY_AMBIENT_ON; +#endif + r = minnow_panel_change_state_mlocked(mpd, state); mutex_unlock(&mpd->lock); return r; } @@ -3726,6 +3838,7 @@ static struct omap_dss_driver minnow_panel_driver = { .sync = minnow_panel_sync, .get_resolution = minnow_panel_get_resolution, + .get_dimensions = minnow_panel_get_dimensions, .get_recommended_bpp = omapdss_default_get_recommended_bpp, .enable_te = minnow_panel_enable_te, |