diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index fd8d0d594fc7..e0eb9d287312 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -369,6 +369,8 @@ is not associated with a file: [stack:1001] = the stack of the thread with tid 1001 [vdso] = the "virtual dynamic shared object", the kernel system call handler + [anon:] = an anonymous mapping that has been + named by userspace or if empty, the mapping is anonymous. @@ -419,6 +421,7 @@ KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 374 kB VmFlags: rd ex mr mw me de +Name: name from userspace the first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping @@ -469,6 +472,9 @@ Note that there is no guarantee that every flag and associated mnemonic will be present in all further kernel releases. Things get changed, the flags may be vanished or the reverse -- new added. +The "Name" field will only be present on a mapping that has been named by +userspace, and will show the name passed in by userspace. + This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index dcc75a9ed919..b81fca90f7fe 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm: - dirty_writeback_centisecs - drop_caches - extfrag_threshold +- extra_free_kbytes - hugepages_treat_as_movable - hugetlb_shm_group - laptop_mode @@ -198,6 +199,21 @@ fragmentation index is <= extfrag_threshold. The default value is 500. ============================================================== +extra_free_kbytes + +This parameter tells the VM to keep extra free memory between the threshold +where background reclaim (kswapd) kicks in, and the threshold where direct +reclaim (by allocating processes) kicks in. + +This is useful for workloads that require low latency memory allocations +and have a bounded burstiness in memory allocations, for example a +realtime application that receives and transmits network traffic +(causing in-kernel memory allocations) with a maximum total message burst +size of 200MB may need 200MB of extra free memory to avoid direct reclaim +related latencies. + +============================================================== + hugepages_treat_as_movable This parameter is only useful when kernelcore= is specified at boot time to diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index ca3cc8613366..546c37f552bc 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -11,7 +11,6 @@ CONFIG_COMPACTION=y CONFIG_ANDROID_RAM_CONSOLE=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BATTERY_ANDROID=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index 778c2f7024ff..b321c8fbb87d 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -144,6 +144,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) { + if (user_mode(regs)) + return -1; kgdb_handle_exception(1, SIGTRAP, 0, regs); return 0; @@ -151,6 +153,8 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr) { + if (user_mode(regs)) + return -1; compiled_break = 1; kgdb_handle_exception(1, SIGTRAP, 0, regs); diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index a494fbd7dbe8..12376ae07de8 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -61,6 +61,7 @@ static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; static struct mutex gov_lock; +/* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; @@ -71,31 +72,25 @@ static unsigned int default_above_hispeed_delay[] = { struct cpufreq_interactive_tunables { int usage_count; - /* Hi speed to bump to from lo speed when load burst (default max) */ unsigned int hispeed_freq; - /* Go to hi speed when CPU load at or above this value. */ #define DEFAULT_GO_HISPEED_LOAD 99 unsigned long go_hispeed_load; - /* Target load. Lower values result in higher CPU speeds. */ spinlock_t target_loads_lock; unsigned int *target_loads; int ntarget_loads; - /* * The minimum amount of time to spend at a frequency before we can ramp * down. */ #define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) unsigned long min_sample_time; - /* * The sample rate of the timer used to increase frequency */ unsigned long timer_rate; - /* * Wait this long before raising speed above hispeed, by default a * single timer interval. @@ -103,21 +98,18 @@ struct cpufreq_interactive_tunables { spinlock_t above_hispeed_delay_lock; unsigned int *above_hispeed_delay; int nabove_hispeed_delay; - /* Non-zero means indefinite speed boost active */ int boost_val; - /* Duration of a boost pulse in usecs */ + /* Duration of a boot pulse in usecs */ int boostpulse_duration_val; /* End time of boost pulse in ktime converted to usecs */ u64 boostpulse_endtime; - /* * Max additional time to wait in idle, beyond timer_rate, at speeds * above minimum before wakeup to reduce speed, or -1 if unnecessary. */ #define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) int timer_slack_val; - bool io_is_busy; }; @@ -137,15 +129,15 @@ static void cpufreq_interactive_timer_resched( spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = get_cpu_idle_time(smp_processor_id(), - &pcpu->time_in_idle_timestamp, - tunables->io_is_busy); + &pcpu->time_in_idle_timestamp, + tunables->io_is_busy); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; expires = jiffies + usecs_to_jiffies(tunables->timer_rate); mod_timer_pinned(&pcpu->cpu_timer, expires); if (tunables->timer_slack_val >= 0 && - pcpu->target_freq > pcpu->policy->min) { + pcpu->target_freq > pcpu->policy->min) { expires += usecs_to_jiffies(tunables->timer_slack_val); mod_timer_pinned(&pcpu->cpu_slack_timer, expires); } @@ -158,16 +150,17 @@ static void cpufreq_interactive_timer_resched( * function. */ static void cpufreq_interactive_timer_start( - struct cpufreq_interactive_tunables *tunables, int cpu) + struct cpufreq_interactive_tunables *tunables, int cpu) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); - unsigned long expires = jiffies + usecs_to_jiffies(tunables->timer_rate); + unsigned long expires = jiffies + + usecs_to_jiffies(tunables->timer_rate); unsigned long flags; pcpu->cpu_timer.expires = expires; add_timer_on(&pcpu->cpu_timer, cpu); if (tunables->timer_slack_val >= 0 && - pcpu->target_freq > pcpu->policy->min) { + pcpu->target_freq > pcpu->policy->min) { expires += usecs_to_jiffies(tunables->timer_slack_val); pcpu->cpu_slack_timer.expires = expires; add_timer_on(&pcpu->cpu_slack_timer, cpu); @@ -183,8 +176,8 @@ static void cpufreq_interactive_timer_start( } static unsigned int freq_to_above_hispeed_delay( - struct cpufreq_interactive_tunables *tunables, - unsigned int freq) + struct cpufreq_interactive_tunables *tunables, + unsigned int freq) { int i; unsigned int ret; @@ -201,8 +194,8 @@ static unsigned int freq_to_above_hispeed_delay( return ret; } -static unsigned int freq_to_targetload(struct cpufreq_interactive_tunables - *tunables, unsigned int freq) +static unsigned int freq_to_targetload( + struct cpufreq_interactive_tunables *tunables, unsigned int freq) { int i; unsigned int ret; @@ -211,7 +204,7 @@ static unsigned int freq_to_targetload(struct cpufreq_interactive_tunables spin_lock_irqsave(&tunables->target_loads_lock, flags); for (i = 0; i < tunables->ntarget_loads - 1 && - freq >= tunables->target_loads[i+1]; i += 2) + freq >= tunables->target_loads[i+1]; i += 2) ; ret = tunables->target_loads[i]; @@ -706,8 +699,9 @@ err: return ERR_PTR(err); } -static ssize_t show_target_loads(struct cpufreq_interactive_tunables *tunables, - char *buf) +static ssize_t show_target_loads( + struct cpufreq_interactive_tunables *tunables, + char *buf) { int i; ssize_t ret = 0; @@ -719,13 +713,14 @@ static ssize_t show_target_loads(struct cpufreq_interactive_tunables *tunables, ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + ret, "\n"); + ret += sprintf(buf + --ret, "\n"); spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return ret; } -static ssize_t store_target_loads(struct cpufreq_interactive_tunables *tunables, - const char *buf, size_t count) +static ssize_t store_target_loads( + struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ntokens; unsigned int *new_target_loads = NULL; @@ -744,8 +739,8 @@ static ssize_t store_target_loads(struct cpufreq_interactive_tunables *tunables, return count; } -static ssize_t show_above_hispeed_delay(struct cpufreq_interactive_tunables - *tunables, char *buf) +static ssize_t show_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, char *buf) { int i; ssize_t ret = 0; @@ -758,13 +753,14 @@ static ssize_t show_above_hispeed_delay(struct cpufreq_interactive_tunables tunables->above_hispeed_delay[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + ret, "\n"); + ret += sprintf(buf + --ret, "\n"); spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); return ret; } -static ssize_t store_above_hispeed_delay(struct cpufreq_interactive_tunables - *tunables, const char *buf, size_t count) +static ssize_t store_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ntokens; unsigned int *new_above_hispeed_delay = NULL; @@ -1012,6 +1008,18 @@ store_gov_pol_sys(boostpulse); show_store_gov_pol_sys(boostpulse_duration); show_store_gov_pol_sys(io_is_busy); +#define gov_sys_attr_rw(_name) \ +static struct global_attr _name##_gov_sys = \ +__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys) + +#define gov_pol_attr_rw(_name) \ +static struct freq_attr _name##_gov_pol = \ +__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol) + +#define gov_sys_pol_attr_rw(_name) \ + gov_sys_attr_rw(_name); \ + gov_pol_attr_rw(_name) + gov_sys_pol_attr_rw(target_loads); gov_sys_pol_attr_rw(above_hispeed_delay); gov_sys_pol_attr_rw(hispeed_freq); @@ -1200,6 +1208,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->hispeed_validate_time = pcpu->floor_validate_time; down_write(&pcpu->enable_sem); + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); cpufreq_interactive_timer_start(tunables, j); pcpu->governor_enabled = 1; up_write(&pcpu->enable_sem); diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index 306fff970de4..9c956659124b 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,3 +1,6 @@ obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \ - ion_carveout_heap.o ion_chunk_heap.o + ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o +ifdef CONFIG_COMPAT +obj-$(CONFIG_ION) += compat_ion.o +endif obj-$(CONFIG_ION_TEGRA) += tegra/ diff --git a/drivers/gpu/ion/compat_ion.c b/drivers/gpu/ion/compat_ion.c new file mode 100644 index 000000000000..e0d2839952a0 --- /dev/null +++ b/drivers/gpu/ion/compat_ion.c @@ -0,0 +1,172 @@ +/* + * drivers/gpu/ion/compat_ion.c + * + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include "compat_ion.h" + +/* See include/linux/ion.h for the definition of these structs */ +struct compat_ion_allocation_data { + compat_size_t len; + compat_size_t align; + compat_uint_t heap_id_mask; + compat_uint_t flags; + compat_int_t handle; +}; + +struct compat_ion_custom_data { + compat_uint_t cmd; + compat_ulong_t arg; +}; + +static int compat_get_ion_allocation_data( + struct compat_ion_allocation_data __user *data32, + struct ion_allocation_data __user *data) +{ + compat_size_t s; + compat_uint_t u; + compat_int_t i; + int err; + + err = get_user(s, &data32->len); + err |= put_user(s, &data->len); + err |= get_user(s, &data32->align); + err |= put_user(s, &data->align); + err |= get_user(u, &data32->heap_id_mask); + err |= put_user(u, &data->heap_id_mask); + err |= get_user(u, &data32->flags); + err |= put_user(u, &data->flags); + err |= get_user(i, &data32->handle); + err |= put_user(i, &data->handle); + + return err; +} + +static int compat_put_ion_allocation_data( + struct compat_ion_allocation_data __user *data32, + struct ion_allocation_data __user *data) +{ + compat_size_t s; + compat_uint_t u; + compat_int_t i; + int err; + + err = get_user(s, &data->len); + err |= put_user(s, &data32->len); + err |= get_user(s, &data->align); + err |= put_user(s, &data32->align); + err |= get_user(u, &data->heap_id_mask); + err |= put_user(u, &data32->heap_id_mask); + err |= get_user(u, &data->flags); + err |= put_user(u, &data32->flags); + err |= get_user(i, &data->handle); + err |= put_user(i, &data32->handle); + + return err; +} + +static int compat_get_ion_custom_data( + struct compat_ion_custom_data __user *data32, + struct ion_custom_data __user *data) +{ + compat_uint_t cmd; + compat_ulong_t arg; + int err; + + err = get_user(cmd, &data32->cmd); + err |= put_user(cmd, &data->cmd); + err |= get_user(arg, &data32->arg); + err |= put_user(arg, &data->arg); + + return err; +}; + +long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret; + + if (!filp->f_op || !filp->f_op->unlocked_ioctl) + return -ENOTTY; + + switch (cmd) { + case ION_IOC_ALLOC: + { + struct compat_ion_allocation_data __user *data32; + struct ion_allocation_data __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (data == NULL) + return -EFAULT; + + err = compat_get_ion_allocation_data(data32, data); + if (err) + return err; + + ret = filp->f_op->unlocked_ioctl(filp, cmd, + (unsigned long)data); + err = compat_put_ion_allocation_data(data32, data); + return ret ? ret : err; + } + case ION_IOC_FREE: + { + struct compat_ion_allocation_data __user *data32; + struct ion_allocation_data __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (data == NULL) + return -EFAULT; + + err = compat_get_ion_allocation_data(data32, data); + if (err) + return err; + + return filp->f_op->unlocked_ioctl(filp, cmd, + (unsigned long)data); + } + case ION_IOC_CUSTOM: { + struct compat_ion_custom_data __user *data32; + struct ion_custom_data __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (data == NULL) + return -EFAULT; + + err = compat_get_ion_custom_data(data32, data); + if (err) + return err; + + return filp->f_op->unlocked_ioctl(filp, cmd, + (unsigned long)data); + } + case ION_IOC_SHARE: + case ION_IOC_MAP: + case ION_IOC_IMPORT: + case ION_IOC_SYNC: + return filp->f_op->unlocked_ioctl(filp, cmd, + (unsigned long)compat_ptr(arg)); + default: + return -ENOIOCTLCMD; + } +} diff --git a/drivers/gpu/ion/compat_ion.h b/drivers/gpu/ion/compat_ion.h new file mode 100644 index 000000000000..3a9c8c08c240 --- /dev/null +++ b/drivers/gpu/ion/compat_ion.h @@ -0,0 +1,30 @@ +/* + + * drivers/gpu/ion/compat_ion.h + * + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_COMPAT_ION_H +#define _LINUX_COMPAT_ION_H + +#if IS_ENABLED(CONFIG_COMPAT) + +long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +#else + +#define compat_ion_ioctl NULL + +#endif /* CONFIG_COMPAT */ +#endif /* _LINUX_COMPAT_ION_H */ diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 6c93365c3dbc..e4ffc9d5b94b 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -32,10 +32,13 @@ #include #include #include +#include #include #include +#include #include "ion_priv.h" +#include "compat_ion.h" /** * struct ion_device - the metadata of the ion device node @@ -63,6 +66,7 @@ struct ion_device { * @node: node in the tree of all clients * @dev: backpointer to ion device * @handles: an rb tree of all the handles in this client + * @idr: an idr space for allocating handle ids * @lock: lock protecting the tree of handles * @name: used for debugging * @task: used for debugging @@ -75,6 +79,7 @@ struct ion_client { struct rb_node node; struct ion_device *dev; struct rb_root handles; + struct idr idr; struct mutex lock; const char *name; struct task_struct *task; @@ -89,7 +94,7 @@ struct ion_client { * @buffer: pointer to the buffer * @node: node in the client's handle rbtree * @kmap_cnt: count of times this client has mapped to kernel - * @dmap_cnt: count of times this client has mapped for dma + * @id: client-unique id allocated by client->idr * * Modifications to node, map_cnt or mapping should be protected by the * lock in the client. Other fields are never changed after initialization. @@ -100,17 +105,38 @@ struct ion_handle { struct ion_buffer *buffer; struct rb_node node; unsigned int kmap_cnt; + int id; }; bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) { - return ((buffer->flags & ION_FLAG_CACHED) && - !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); + return ((buffer->flags & ION_FLAG_CACHED) && + !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); } bool ion_buffer_cached(struct ion_buffer *buffer) { - return !!(buffer->flags & ION_FLAG_CACHED); + return !!(buffer->flags & ION_FLAG_CACHED); +} + +static inline struct page *ion_buffer_page(struct page *page) +{ + return (struct page *)((unsigned long)page & ~(1UL)); +} + +static inline bool ion_buffer_page_is_dirty(struct page *page) +{ + return !!((unsigned long)page & 1UL); +} + +static inline void ion_buffer_page_dirty(struct page **page) +{ + *page = (struct page *)((unsigned long)(*page) | 1UL); +} + +static inline void ion_buffer_page_clean(struct page **page) +{ + *page = (struct page *)((unsigned long)(*page) & ~(1UL)); } /* this function should only be called while dev->lock is held */ @@ -139,8 +165,6 @@ static void ion_buffer_add(struct ion_device *dev, rb_insert_color(&buffer->node, &dev->buffers); } -static int ion_buffer_alloc_dirty(struct ion_buffer *buffer); - /* this function should only be called while dev->lock is held */ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, struct ion_device *dev, @@ -178,24 +202,32 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, buffer->size = len; table = heap->ops->map_dma(heap, buffer); - if (IS_ERR_OR_NULL(table)) { + if (WARN_ONCE(table == NULL, "heap->ops->map_dma should return ERR_PTR on error")) + table = ERR_PTR(-EINVAL); + if (IS_ERR(table)) { heap->ops->free(buffer); kfree(buffer); return ERR_PTR(PTR_ERR(table)); } buffer->sg_table = table; if (ion_buffer_fault_user_mappings(buffer)) { - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, - i) { - if (sg_dma_len(sg) == PAGE_SIZE) - continue; - pr_err("%s: cached mappings that will be faulted in " - "must have pagewise sg_lists\n", __func__); - ret = -EINVAL; - goto err; + int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + struct scatterlist *sg; + int i, j, k = 0; + + buffer->pages = vmalloc(sizeof(struct page *) * num_pages); + if (!buffer->pages) { + ret = -ENOMEM; + goto err1; + } + + for_each_sg(table->sgl, sg, table->nents, i) { + struct page *page = sg_page(sg); + + for (j = 0; j < sg_dma_len(sg) / PAGE_SIZE; j++) + buffer->pages[k++] = page++; } - ret = ion_buffer_alloc_dirty(buffer); if (ret) goto err; } @@ -222,6 +254,9 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, err: heap->ops->unmap_dma(heap, buffer); heap->ops->free(buffer); +err1: + if (buffer->pages) + vfree(buffer->pages); err2: kfree(buffer); return ERR_PTR(ret); @@ -233,8 +268,8 @@ void ion_buffer_destroy(struct ion_buffer *buffer) buffer->heap->ops->unmap_kernel(buffer->heap, buffer); buffer->heap->ops->unmap_dma(buffer->heap, buffer); buffer->heap->ops->free(buffer); - if (buffer->flags & ION_FLAG_CACHED) - kfree(buffer->dirty); + if (buffer->pages) + vfree(buffer->pages); kfree(buffer); } @@ -326,6 +361,7 @@ static void ion_handle_destroy(struct kref *kref) ion_handle_kmap_put(handle); mutex_unlock(&buffer->lock); + idr_remove(&client->idr, handle->id); if (!RB_EMPTY_NODE(&handle->node)) rb_erase(&handle->node, &client->handles); @@ -353,47 +389,56 @@ static int ion_handle_put(struct ion_handle *handle) static struct ion_handle *ion_handle_lookup(struct ion_client *client, struct ion_buffer *buffer) { - struct rb_node *n; + struct rb_node *n = client->handles.rb_node; - for (n = rb_first(&client->handles); n; n = rb_next(n)) { - struct ion_handle *handle = rb_entry(n, struct ion_handle, - node); - if (handle->buffer == buffer) - return handle; + while (n) { + struct ion_handle *entry = rb_entry(n, struct ion_handle, node); + if (buffer < entry->buffer) + n = n->rb_left; + else if (buffer > entry->buffer) + n = n->rb_right; + else + return entry; } - return NULL; + return ERR_PTR(-EINVAL); +} + +static struct ion_handle *ion_uhandle_get(struct ion_client *client, int id) +{ + return idr_find(&client->idr, id); } static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) { - struct rb_node *n = client->handles.rb_node; - - while (n) { - struct ion_handle *handle_node = rb_entry(n, struct ion_handle, - node); - if (handle < handle_node) - n = n->rb_left; - else if (handle > handle_node) - n = n->rb_right; - else - return true; - } - return false; + return (ion_uhandle_get(client, handle->id) == handle); } -static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) +static int ion_handle_add(struct ion_client *client, struct ion_handle *handle) { + int rc; struct rb_node **p = &client->handles.rb_node; struct rb_node *parent = NULL; struct ion_handle *entry; + do { + int id; + rc = idr_pre_get(&client->idr, GFP_KERNEL); + if (!rc) + return -ENOMEM; + rc = idr_get_new_above(&client->idr, handle, 1, &id); + handle->id = id; + } while (rc == -EAGAIN); + + if (rc < 0) + return rc; + while (*p) { parent = *p; entry = rb_entry(parent, struct ion_handle, node); - if (handle < entry) + if (handle->buffer < entry->buffer) p = &(*p)->rb_left; - else if (handle > entry) + else if (handle->buffer > entry->buffer) p = &(*p)->rb_right; else WARN(1, "%s: buffer already found.", __func__); @@ -401,6 +446,8 @@ static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) rb_link_node(&handle->node, parent, p); rb_insert_color(&handle->node, &client->handles); + + return 0; } struct ion_handle *ion_alloc(struct ion_client *client, size_t len, @@ -411,6 +458,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, struct ion_device *dev = client->dev; struct ion_buffer *buffer = NULL; struct ion_heap *heap; + int ret; pr_debug("%s: len %d align %d heap_id_mask %u flags %x\n", __func__, len, align, heap_id_mask, flags); @@ -431,7 +479,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, if (!((1 << heap->id) & heap_id_mask)) continue; buffer = ion_buffer_create(heap, dev, len, align, flags); - if (!IS_ERR_OR_NULL(buffer)) + if (!IS_ERR(buffer)) break; } up_read(&dev->lock); @@ -450,12 +498,16 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, */ ion_buffer_put(buffer); - if (!IS_ERR(handle)) { - mutex_lock(&client->lock); - ion_handle_add(client, handle); - mutex_unlock(&client->lock); - } + if (IS_ERR(handle)) + return handle; + mutex_lock(&client->lock); + ret = ion_handle_add(client, handle); + if (ret) { + ion_handle_put(handle); + handle = ERR_PTR(ret); + } + mutex_unlock(&client->lock); return handle; } @@ -515,7 +567,9 @@ static void *ion_buffer_kmap_get(struct ion_buffer *buffer) return buffer->vaddr; } vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); - if (IS_ERR_OR_NULL(vaddr)) + if (WARN_ONCE(vaddr == NULL, "heap->ops->map_kernel should return ERR_PTR on error")) + return ERR_PTR(-EINVAL); + if (IS_ERR(vaddr)) return vaddr; buffer->vaddr = vaddr; buffer->kmap_cnt++; @@ -532,7 +586,7 @@ static void *ion_handle_kmap_get(struct ion_handle *handle) return buffer->vaddr; } vaddr = ion_buffer_kmap_get(buffer); - if (IS_ERR_OR_NULL(vaddr)) + if (IS_ERR(vaddr)) return vaddr; handle->kmap_cnt++; return vaddr; @@ -673,6 +727,7 @@ struct ion_client *ion_client_create(struct ion_device *dev, client->dev = dev; client->handles = RB_ROOT; + idr_init(&client->idr); mutex_init(&client->lock); client->name = name; client->task = task; @@ -713,6 +768,10 @@ void ion_client_destroy(struct ion_client *client) node); ion_handle_destroy(&handle->ref); } + + idr_remove_all(&client->idr); + idr_destroy(&client->idr); + down_write(&dev->lock); if (client->task) put_task_struct(client->task); @@ -764,17 +823,6 @@ static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, { } -static int ion_buffer_alloc_dirty(struct ion_buffer *buffer) -{ - unsigned long pages = buffer->sg_table->nents; - unsigned long length = (pages + BITS_PER_LONG - 1)/BITS_PER_LONG; - - buffer->dirty = kzalloc(length * sizeof(unsigned long), GFP_KERNEL); - if (!buffer->dirty) - return -ENOMEM; - return 0; -} - struct ion_vma_list { struct list_head list; struct vm_area_struct *vma; @@ -784,9 +832,9 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, struct device *dev, enum dma_data_direction dir) { - struct scatterlist *sg; - int i; struct ion_vma_list *vma_list; + int pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + int i; pr_debug("%s: syncing for device %s\n", __func__, dev ? dev_name(dev) : "null"); @@ -795,11 +843,12 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, return; mutex_lock(&buffer->lock); - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { - if (!test_bit(i, buffer->dirty)) - continue; - dma_sync_sg_for_device(dev, sg, 1, dir); - clear_bit(i, buffer->dirty); + for (i = 0; i < pages; i++) { + struct page *page = buffer->pages[i]; + + if (ion_buffer_page_is_dirty(page)) + __dma_page_cpu_to_dev(page, 0, PAGE_SIZE, dir); + ion_buffer_page_clean(buffer->pages + i); } list_for_each_entry(vma_list, &buffer->vmas, list) { struct vm_area_struct *vma = vma_list->vma; @@ -813,21 +862,18 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct ion_buffer *buffer = vma->vm_private_data; - struct scatterlist *sg; - int i; + int ret; mutex_lock(&buffer->lock); - set_bit(vmf->pgoff, buffer->dirty); + ion_buffer_page_dirty(buffer->pages + vmf->pgoff); - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { - if (i != vmf->pgoff) - continue; - dma_sync_sg_for_cpu(NULL, sg, 1, DMA_BIDIRECTIONAL); - vm_insert_page(vma, (unsigned long)vmf->virtual_address, - sg_page(sg)); - break; - } + BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]); + ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, + ion_buffer_page(buffer->pages[vmf->pgoff])); mutex_unlock(&buffer->lock); + if (ret) + return VM_FAULT_ERROR; + return VM_FAULT_NOPAGE; } @@ -939,8 +985,6 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start, mutex_unlock(&buffer->lock); if (IS_ERR(vaddr)) return PTR_ERR(vaddr); - if (!vaddr) - return -ENOMEM; return 0; } @@ -1017,9 +1061,10 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) struct dma_buf *dmabuf; struct ion_buffer *buffer; struct ion_handle *handle; + int ret; dmabuf = dma_buf_get(fd); - if (IS_ERR_OR_NULL(dmabuf)) + if (IS_ERR(dmabuf)) return ERR_PTR(PTR_ERR(dmabuf)); /* if this memory came from ion */ @@ -1034,14 +1079,18 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) mutex_lock(&client->lock); /* if a handle exists for this buffer just take a reference to it */ handle = ion_handle_lookup(client, buffer); - if (!IS_ERR_OR_NULL(handle)) { + if (!IS_ERR(handle)) { ion_handle_get(handle); goto end; } handle = ion_handle_create(client, buffer); - if (IS_ERR_OR_NULL(handle)) + if (IS_ERR(handle)) goto end; - ion_handle_add(client, handle); + ret = ion_handle_add(client, handle); + if (ret) { + ion_handle_put(handle); + handle = ERR_PTR(ret); + } end: mutex_unlock(&client->lock); dma_buf_put(dmabuf); @@ -1055,7 +1104,7 @@ static int ion_sync_for_device(struct ion_client *client, int fd) struct ion_buffer *buffer; dmabuf = dma_buf_get(fd); - if (IS_ERR_OR_NULL(dmabuf)) + if (IS_ERR(dmabuf)) return PTR_ERR(dmabuf); /* if this memory came from ion */ @@ -1081,17 +1130,20 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_ALLOC: { struct ion_allocation_data data; + struct ion_handle *handle; if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; - data.handle = ion_alloc(client, data.len, data.align, + handle = ion_alloc(client, data.len, data.align, data.heap_id_mask, data.flags); - if (IS_ERR(data.handle)) - return PTR_ERR(data.handle); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + data.handle = handle->id; if (copy_to_user((void __user *)arg, &data, sizeof(data))) { - ion_free(client, data.handle); + ion_free(client, handle); return -EFAULT; } break; @@ -1099,27 +1151,29 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_FREE: { struct ion_handle_data data; - bool valid; + struct ion_handle *handle; if (copy_from_user(&data, (void __user *)arg, sizeof(struct ion_handle_data))) return -EFAULT; mutex_lock(&client->lock); - valid = ion_handle_validate(client, data.handle); + handle = ion_uhandle_get(client, data.handle); mutex_unlock(&client->lock); - if (!valid) + if (!handle) return -EINVAL; - ion_free(client, data.handle); + ion_free(client, handle); break; } case ION_IOC_SHARE: case ION_IOC_MAP: { struct ion_fd_data data; + struct ion_handle *handle; if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; - data.fd = ion_share_dma_buf_fd(client, data.handle); + handle = ion_uhandle_get(client, data.handle); + data.fd = ion_share_dma_buf_fd(client, handle); if (copy_to_user((void __user *)arg, &data, sizeof(data))) return -EFAULT; if (data.fd < 0) @@ -1129,15 +1183,17 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_IMPORT: { struct ion_fd_data data; + struct ion_handle *handle; int ret = 0; if (copy_from_user(&data, (void __user *)arg, sizeof(struct ion_fd_data))) return -EFAULT; - data.handle = ion_import_dma_buf(client, data.fd); - if (IS_ERR(data.handle)) { - ret = PTR_ERR(data.handle); - data.handle = NULL; - } + handle = ion_import_dma_buf(client, data.fd); + if (IS_ERR(handle)) + ret = PTR_ERR(handle); + else + data.handle = handle->id; + if (copy_to_user((void __user *)arg, &data, sizeof(struct ion_fd_data))) return -EFAULT; @@ -1189,7 +1245,7 @@ static int ion_open(struct inode *inode, struct file *file) pr_debug("%s: %d\n", __func__, __LINE__); client = ion_client_create(dev, "user"); - if (IS_ERR_OR_NULL(client)) + if (IS_ERR(client)) return PTR_ERR(client); file->private_data = client; @@ -1201,6 +1257,7 @@ static const struct file_operations ion_fops = { .open = ion_open, .release = ion_release, .unlocked_ioctl = ion_ioctl, + .compat_ioctl = compat_ion_ioctl, }; static size_t ion_debug_heap_total(struct ion_client *client, @@ -1271,6 +1328,9 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) seq_printf(s, "%16.s %16u\n", "total orphaned", total_orphaned_size); seq_printf(s, "%16.s %16u\n", "total ", total_size); + if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) + seq_printf(s, "%16.s %16u\n", "deferred free", + heap->free_list_size); seq_printf(s, "----------------------------------------------------\n"); if (heap->debug_show) @@ -1382,7 +1442,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl) } idev->debug_root = debugfs_create_dir("ion", NULL); - if (IS_ERR_OR_NULL(idev->debug_root)) + if (!idev->debug_root) pr_err("ion: failed to create debug files.\n"); idev->custom_ioctl = custom_ioctl; diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c index ce8d311968f6..86f35545eaf7 100644 --- a/drivers/gpu/ion/ion_carveout_heap.c +++ b/drivers/gpu/ion/ion_carveout_heap.c @@ -112,13 +112,18 @@ void ion_carveout_heap_unmap_dma(struct ion_heap *heap, void *ion_carveout_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) { + void *ret; int mtype = MT_MEMORY_NONCACHED; if (buffer->flags & ION_FLAG_CACHED) mtype = MT_MEMORY; - return __arm_ioremap(buffer->priv_phys, buffer->size, + ret = __arm_ioremap(buffer->priv_phys, buffer->size, mtype); + if (ret == NULL) + return ERR_PTR(-ENOMEM); + + return ret; } void ion_carveout_heap_unmap_kernel(struct ion_heap *heap, diff --git a/drivers/gpu/ion/ion_chunk_heap.c b/drivers/gpu/ion/ion_chunk_heap.c index 8c8f5c3c5537..cd01aad6ac2d 100644 --- a/drivers/gpu/ion/ion_chunk_heap.c +++ b/drivers/gpu/ion/ion_chunk_heap.c @@ -47,15 +47,15 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap, struct scatterlist *sg; int ret, i; unsigned long num_chunks; + unsigned long allocated_size; if (ion_buffer_fault_user_mappings(buffer)) return -ENOMEM; - num_chunks = ALIGN(size, chunk_heap->chunk_size) / - chunk_heap->chunk_size; - buffer->size = num_chunks * chunk_heap->chunk_size; + allocated_size = ALIGN(size, chunk_heap->chunk_size); + num_chunks = allocated_size / chunk_heap->chunk_size; - if (buffer->size > chunk_heap->size - chunk_heap->allocated) + if (allocated_size > chunk_heap->size - chunk_heap->allocated) return -ENOMEM; table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); @@ -78,7 +78,7 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap, } buffer->priv_virt = table; - chunk_heap->allocated += buffer->size; + chunk_heap->allocated += allocated_size; return 0; err: sg = table->sgl; @@ -100,6 +100,9 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer) struct sg_table *table = buffer->priv_virt; struct scatterlist *sg; int i; + unsigned long allocated_size; + + allocated_size = ALIGN(buffer->size, chunk_heap->chunk_size); ion_heap_buffer_zero(buffer); @@ -111,7 +114,7 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer) gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)), sg_dma_len(sg)); } - chunk_heap->allocated -= buffer->size; + chunk_heap->allocated -= allocated_size; sg_free_table(table); kfree(table); } diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c new file mode 100644 index 000000000000..1eaa8c11e04c --- /dev/null +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -0,0 +1,205 @@ +/* + * drivers/gpu/ion/ion_cma_heap.c + * + * Copyright (C) Linaro 2012 + * Author: for ST-Ericsson. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +/* for ion_heap_ops structure */ +#include "ion_priv.h" + +#define ION_CMA_ALLOCATE_FAILED -1 + +struct ion_cma_heap { + struct ion_heap heap; + struct device *dev; +}; + +#define to_cma_heap(x) container_of(x, struct ion_cma_heap, heap) + +struct ion_cma_buffer_info { + void *cpu_addr; + dma_addr_t handle; + struct sg_table *table; +}; + +/* + * Create scatter-list for the already allocated DMA buffer. + * This function could be replaced by dma_common_get_sgtable + * as soon as it will avalaible. + */ +int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size) +{ + struct page *page = virt_to_page(cpu_addr); + int ret; + + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (unlikely(ret)) + return ret; + + sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); + return 0; +} + +/* ION CMA heap operations functions */ +static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, + unsigned long len, unsigned long align, + unsigned long flags) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info; + + dev_dbg(dev, "Request buffer allocation len %ld\n", len); + + info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate buffer info\n"); + return ION_CMA_ALLOCATE_FAILED; + } + + info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0); + + if (!info->cpu_addr) { + dev_err(dev, "Fail to allocate buffer\n"); + goto err; + } + + info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!info->table) { + dev_err(dev, "Fail to allocate sg table\n"); + goto free_mem; + } + + if (ion_cma_get_sgtable + (dev, info->table, info->cpu_addr, info->handle, len)) + goto free_table; + /* keep this for memory release */ + buffer->priv_virt = info; + dev_dbg(dev, "Allocate buffer %p\n", buffer); + return 0; + +free_table: + kfree(info->table); +free_mem: + dma_free_coherent(dev, len, info->cpu_addr, info->handle); +err: + kfree(info); + return ION_CMA_ALLOCATE_FAILED; +} + +static void ion_cma_free(struct ion_buffer *buffer) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Release buffer %p\n", buffer); + /* release memory */ + dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); + /* release sg table */ + sg_free_table(info->table); + kfree(info->table); + kfree(info); +} + +/* return physical address in addr */ +static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer, + info->handle); + + *addr = info->handle; + *len = buffer->size; + + return 0; +} + +struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_cma_buffer_info *info = buffer->priv_virt; + + return info->table; +} + +void ion_cma_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return; +} + +static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle, + buffer->size); +} + +void *ion_cma_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) +{ + struct ion_cma_buffer_info *info = buffer->priv_virt; + /* kernel memory mapping has been done at allocation time */ + return info->cpu_addr; +} + +static struct ion_heap_ops ion_cma_ops = { + .allocate = ion_cma_allocate, + .free = ion_cma_free, + .map_dma = ion_cma_heap_map_dma, + .unmap_dma = ion_cma_heap_unmap_dma, + .phys = ion_cma_phys, + .map_user = ion_cma_mmap, + .map_kernel = ion_cma_map_kernel, +}; + +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) +{ + struct ion_cma_heap *cma_heap; + + cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL); + + if (!cma_heap) + return ERR_PTR(-ENOMEM); + + cma_heap->heap.ops = &ion_cma_ops; + /* get device from private heaps data, later it will be + * used to make the link with reserved CMA memory */ + cma_heap->dev = data->priv; + cma_heap->heap.type = ION_HEAP_TYPE_DMA; + return &cma_heap->heap; +} + +void ion_cma_heap_destroy(struct ion_heap *heap) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(heap); + + kfree(cma_heap); +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 4a16aa27384c..786302de7ed0 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -56,6 +56,9 @@ void *ion_heap_map_kernel(struct ion_heap *heap, vaddr = vmap(pages, npages, VM_MAP, pgprot); vfree(pages); + if (vaddr == NULL) + return ERR_PTR(-ENOMEM); + return vaddr; } @@ -134,8 +137,22 @@ end: return ret; } -void ion_heap_free_page(struct ion_buffer *buffer, struct page *page, - unsigned int order) +struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags, + unsigned int order) +{ + struct page *page = alloc_pages(gfp_flags, order); + + if (!page) + return page; + + if (ion_buffer_fault_user_mappings(buffer)) + split_page(page, order); + + return page; +} + +void ion_heap_free_pages(struct ion_buffer *buffer, struct page *page, + unsigned int order) { int i; @@ -254,6 +271,9 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) case ION_HEAP_TYPE_CHUNK: heap = ion_chunk_heap_create(heap_data); break; + case ION_HEAP_TYPE_DMA: + heap = ion_cma_heap_create(heap_data); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap_data->type); @@ -290,6 +310,9 @@ void ion_heap_destroy(struct ion_heap *heap) case ION_HEAP_TYPE_CHUNK: ion_chunk_heap_destroy(heap); break; + case ION_HEAP_TYPE_DMA: + ion_cma_heap_destroy(heap); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap->type); diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index f9bccbc131ed..32461e946735 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -45,9 +45,8 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle); * @vaddr: the kenrel mapping if kmap_cnt is not zero * @dmap_cnt: number of times the buffer is mapped for dma * @sg_table: the sg table for the buffer if dmap_cnt is not zero - * @dirty: bitmask representing which pages of this buffer have - * been dirtied by the cpu and need cache maintenance - * before dma + * @pages: flat array of pages in the buffer -- used by fault + * handler and only valid for buffers that are faulted in * @vmas: list of vma's mapping this buffer * @handle_count: count of handles referencing this buffer * @task_comm: taskcomm of last client to reference this buffer in a @@ -74,7 +73,7 @@ struct ion_buffer { void *vaddr; int dmap_cnt; struct sg_table *sg_table; - unsigned long *dirty; + struct page **pages; struct list_head vmas; /* used to track orphaned buffers */ int handle_count; @@ -94,6 +93,9 @@ void ion_buffer_destroy(struct ion_buffer *buffer); * @map_kernel map memory to the kernel * @unmap_kernel unmap memory to the kernel * @map_user map memory to userspace + * + * allocate, phys, and map_user return 0 on success, -errno on error. + * map_dma and map_kernel return pointer on success, ERR_PTR on error. */ struct ion_heap_ops { int (*allocate) (struct ion_heap *heap, @@ -212,6 +214,19 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *, struct vm_area_struct *); int ion_heap_buffer_zero(struct ion_buffer *buffer); +/** + * ion_heap_alloc_pages - allocate pages from alloc_pages + * @buffer: the buffer to allocate for, used to extract the flags + * @gfp_flags: the gfp_t for the allocation + * @order: the order of the allocatoin + * + * This funciton allocations from alloc pages and also does any other + * necessary operations based on the buffer->flags. For buffers which + * will be faulted in the pages are split using split_page + */ +struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags, + unsigned int order); + /** * ion_heap_init_deferred_free -- initialize deferred free functionality * @heap: the heap @@ -269,6 +284,9 @@ void ion_carveout_heap_destroy(struct ion_heap *); struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *); void ion_chunk_heap_destroy(struct ion_heap *); +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *); +void ion_cma_heap_destroy(struct ion_heap *); + /** * kernel api to allocate/free from carveout -- used when carveout is * used to back an architecture specific custom heap diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c index e101db5da5b4..5fe81a76f2f5 100644 --- a/drivers/gpu/ion/ion_system_heap.c +++ b/drivers/gpu/ion/ion_system_heap.c @@ -64,7 +64,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, unsigned long order) { bool cached = ion_buffer_cached(buffer); - bool split_pages = ion_buffer_fault_user_mappings(buffer); struct ion_page_pool *pool = heap->pools[order_to_index(order)]; struct page *page; @@ -75,7 +74,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, if (order > 4) gfp_flags = high_order_gfp_flags; - page = alloc_pages(gfp_flags, order); + page = ion_heap_alloc_pages(buffer, gfp_flags, order); if (!page) return 0; arm_dma_ops.sync_single_for_device(NULL, @@ -85,8 +84,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, if (!page) return 0; - if (split_pages) - split_page(page, order); return page; } @@ -153,7 +150,6 @@ static int ion_system_heap_allocate(struct ion_heap *heap, int i = 0; long size_remaining = PAGE_ALIGN(size); unsigned int max_order = orders[0]; - bool split_pages = ion_buffer_fault_user_mappings(buffer); INIT_LIST_HEAD(&pages); while (size_remaining > 0) { @@ -170,28 +166,15 @@ static int ion_system_heap_allocate(struct ion_heap *heap, if (!table) goto err; - if (split_pages) - ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE, - GFP_KERNEL); - else - ret = sg_alloc_table(table, i, GFP_KERNEL); - + ret = sg_alloc_table(table, i, GFP_KERNEL); if (ret) goto err1; sg = table->sgl; list_for_each_entry_safe(info, tmp_info, &pages, list) { struct page *page = info->page; - if (split_pages) { - for (i = 0; i < (1 << info->order); i++) { - sg_set_page(sg, page + i, PAGE_SIZE, 0); - sg = sg_next(sg); - } - } else { - sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, - 0); - sg = sg_next(sg); - } + sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0); + sg = sg_next(sg); list_del(&info->list); kfree(info); } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 430ef8c8b6a8..7b8979c63f48 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -204,16 +204,6 @@ config BATTERY_MAX17042 with MAX17042. This driver also supports max17047/50 chips which are improved version of max17042. -config BATTERY_ANDROID - tristate "Battery driver for Android" - help - Say Y to enable generic support for battery charging according - to common Android policies. - This driver adds periodic battery level and health monitoring, - kernel log reporting and other debugging features, common board - battery file glue logic for battery/case temperature sensors, - etc. - config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 27d9deef51b6..653bf6ceff30 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o -obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c deleted file mode 100644 index 8d45ff0f3677..000000000000 --- a/drivers/power/android_battery.c +++ /dev/null @@ -1,692 +0,0 @@ -/* - * android_battery.c - * Android Battery Driver - * - * Copyright (C) 2012 Google, Inc. - * Copyright (C) 2012 Samsung Electronics - * - * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and - * joshua.chang@samsung.com. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FAST_POLL (1 * 60) -#define SLOW_POLL (10 * 60) - -struct android_bat_data { - struct android_bat_platform_data *pdata; - struct android_bat_callbacks callbacks; - - struct device *dev; - - struct power_supply psy_bat; - - struct wake_lock monitor_wake_lock; - struct wake_lock charger_wake_lock; - - int charge_source; - - int batt_temp; - int batt_current; - unsigned int batt_health; - unsigned int batt_vcell; - unsigned int batt_soc; - unsigned int charging_status; - bool recharging; - unsigned long charging_start_time; - - struct workqueue_struct *monitor_wqueue; - struct work_struct monitor_work; - struct work_struct charger_work; - - struct alarm monitor_alarm; - ktime_t last_poll; - - struct dentry *debugfs_entry; -}; - -static enum power_supply_property android_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CURRENT_NOW, -}; - -static DEFINE_MUTEX(android_bat_state_lock); - -static void android_bat_update_data(struct android_bat_data *battery); -static int android_bat_enable_charging(struct android_bat_data *battery, - bool enable); - -static char *charge_source_str(int charge_source) -{ - switch (charge_source) { - case CHARGE_SOURCE_NONE: - return "none"; - case CHARGE_SOURCE_AC: - return "ac"; - case CHARGE_SOURCE_USB: - return "usb"; - default: - break; - } - - return "?"; -} - -static int android_bat_get_property(struct power_supply *ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct android_bat_data *battery = - container_of(ps, struct android_bat_data, psy_bat); - - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = battery->charging_status; - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = battery->batt_health; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = battery->batt_temp; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - android_bat_update_data(battery); - val->intval = battery->batt_vcell; - if (val->intval == -1) - return -EINVAL; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = battery->batt_soc; - if (val->intval == -1) - return -EINVAL; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - android_bat_update_data(battery); - val->intval = battery->batt_current; - break; - default: - return -EINVAL; - } - return 0; -} - -static void android_bat_get_temp(struct android_bat_data *battery) -{ - int batt_temp = 42; /* 4.2C */ - int health = battery->batt_health; - - if (battery->pdata->get_temperature) - battery->pdata->get_temperature(&batt_temp); - - if (battery->charge_source != CHARGE_SOURCE_NONE) { - if (batt_temp >= battery->pdata->temp_high_threshold) { - if (health != POWER_SUPPLY_HEALTH_OVERHEAT && - health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery overheat (%d>=%d), " \ - "charging unavailable\n", - batt_temp, - battery->pdata->temp_high_threshold); - battery->batt_health = - POWER_SUPPLY_HEALTH_OVERHEAT; - } - } else if (batt_temp <= battery->pdata->temp_high_recovery && - batt_temp >= battery->pdata->temp_low_recovery) { - if (health == POWER_SUPPLY_HEALTH_OVERHEAT || - health == POWER_SUPPLY_HEALTH_COLD) { - pr_info("battery recovery (%d,%d~%d)," \ - "charging available\n", - batt_temp, - battery->pdata->temp_low_recovery, - battery->pdata->temp_high_recovery); - battery->batt_health = - POWER_SUPPLY_HEALTH_GOOD; - } - } else if (batt_temp <= battery->pdata->temp_low_threshold) { - if (health != POWER_SUPPLY_HEALTH_COLD && - health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery cold (%d <= %d)," \ - "charging unavailable\n", - batt_temp, - battery->pdata->temp_low_threshold); - battery->batt_health = - POWER_SUPPLY_HEALTH_COLD; - } - } - } - - battery->batt_temp = batt_temp; -} - -/* - * android_bat_state_lock not held, may call back into - * android_bat_charge_source_changed. Gathering data here can be - * non-atomic; updating our state based on the data may need to be - * atomic. - */ - -static void android_bat_update_data(struct android_bat_data *battery) -{ - int ret; - int v; - - if (battery->pdata->poll_charge_source) - battery->charge_source = battery->pdata->poll_charge_source(); - - if (battery->pdata->get_voltage_now) { - ret = battery->pdata->get_voltage_now(); - battery->batt_vcell = ret >= 0 ? ret : 4242000; - } - - if (battery->pdata->get_capacity) { - ret = battery->pdata->get_capacity(); - battery->batt_soc = ret >= 0 ? ret : 42; - } - - if (battery->pdata->get_current_now) { - ret = battery->pdata->get_current_now(&v); - - if (!ret) - battery->batt_current = v; - } - - android_bat_get_temp(battery); -} - -static void android_bat_set_charge_time(struct android_bat_data *battery, - bool enable) -{ - if (enable && !battery->charging_start_time) { - struct timespec cur_time; - - get_monotonic_boottime(&cur_time); - /* record start time for charge timeout timer */ - battery->charging_start_time = cur_time.tv_sec; - } else if (!enable) { - /* clear charge timeout timer */ - battery->charging_start_time = 0; - } -} - -static int android_bat_enable_charging(struct android_bat_data *battery, - bool enable) -{ - if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) { - battery->charging_status = - POWER_SUPPLY_STATUS_NOT_CHARGING; - return -EPERM; - } - - if (enable) { - if (battery->pdata && battery->pdata->set_charging_current) - battery->pdata->set_charging_current - (battery->charge_source); - } - - if (battery->pdata && battery->pdata->set_charging_enable) - battery->pdata->set_charging_enable(enable); - - android_bat_set_charge_time(battery, enable); - pr_info("battery: enable=%d charger: %s\n", enable, - charge_source_str(battery->charge_source)); - return 0; -} - -static bool android_bat_charge_timeout(struct android_bat_data *battery, - unsigned long timeout) -{ - struct timespec cur_time; - - if (!battery->charging_start_time) - return 0; - - get_monotonic_boottime(&cur_time); - pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n", - __func__, battery->charging_start_time, - battery->charging_start_time + timeout, - cur_time.tv_sec); - return cur_time.tv_sec >= battery->charging_start_time + timeout; -} - -static void android_bat_charging_timer(struct android_bat_data *battery) -{ - if (!battery->charging_start_time && - battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) { - android_bat_enable_charging(battery, true); - battery->recharging = true; - pr_debug("%s: charge status charging but timer is expired\n", - __func__); - } else if (battery->charging_start_time == 0) { - pr_debug("%s: charging_start_time never initialized\n", - __func__); - return; - } - - if (android_bat_charge_timeout( - battery, - battery->recharging ? battery->pdata->recharging_time : - battery->pdata->full_charging_time)) { - android_bat_enable_charging(battery, false); - if (battery->batt_vcell > - battery->pdata->recharging_voltage && - battery->batt_soc == 100) - battery->charging_status = - POWER_SUPPLY_STATUS_FULL; - battery->recharging = false; - battery->charging_start_time = 0; - pr_info("battery: charging timer expired\n"); - } - - return; -} - -static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, - int charge_source) -{ - struct android_bat_data *battery = - container_of(ptr, struct android_bat_data, callbacks); - - wake_lock(&battery->charger_wake_lock); - mutex_lock(&android_bat_state_lock); - battery->charge_source = charge_source; - - pr_info("battery: charge source type was changed: %s\n", - charge_source_str(battery->charge_source)); - - mutex_unlock(&android_bat_state_lock); - queue_work(battery->monitor_wqueue, &battery->charger_work); -} - -static void android_bat_set_full_status(struct android_bat_callbacks *ptr) -{ - struct android_bat_data *battery = - container_of(ptr, struct android_bat_data, callbacks); - - mutex_lock(&android_bat_state_lock); - pr_info("battery: battery full\n"); - battery->charging_status = POWER_SUPPLY_STATUS_FULL; - android_bat_enable_charging(battery, false); - battery->recharging = false; - mutex_unlock(&android_bat_state_lock); - power_supply_changed(&battery->psy_bat); -} - -static void android_bat_charger_work(struct work_struct *work) -{ - struct android_bat_data *battery = - container_of(work, struct android_bat_data, charger_work); - - mutex_lock(&android_bat_state_lock); - - switch (battery->charge_source) { - case CHARGE_SOURCE_NONE: - battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; - android_bat_enable_charging(battery, false); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - battery->recharging = false; - battery->charging_start_time = 0; - break; - case CHARGE_SOURCE_USB: - case CHARGE_SOURCE_AC: - /* - * If charging status indicates a charger was already - * connected prior to this and the status is something - * other than charging ("full" or "not-charging"), leave - * the status alone. - */ - if (battery->charging_status == - POWER_SUPPLY_STATUS_DISCHARGING || - battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) - battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; - - /* - * Don't re-enable charging if the battery is full and we - * are not actively re-charging it, or if "not-charging" - * status is set. - */ - if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL - && !battery->recharging) || battery->charging_status == - POWER_SUPPLY_STATUS_NOT_CHARGING)) - android_bat_enable_charging(battery, true); - - break; - default: - pr_err("%s: Invalid charger type\n", __func__); - break; - } - - mutex_unlock(&android_bat_state_lock); - wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); - power_supply_changed(&battery->psy_bat); -} - - -static void android_bat_monitor_set_alarm(struct android_bat_data *battery, - int seconds) -{ - alarm_start(&battery->monitor_alarm, - ktime_add(battery->last_poll, ktime_set(seconds, 0))); -} - -static void android_bat_monitor_work(struct work_struct *work) -{ - struct android_bat_data *battery = - container_of(work, struct android_bat_data, monitor_work); - struct timespec cur_time; - - wake_lock(&battery->monitor_wake_lock); - android_bat_update_data(battery); - mutex_lock(&android_bat_state_lock); - - switch (battery->charging_status) { - case POWER_SUPPLY_STATUS_FULL: - if (battery->batt_vcell < battery->pdata->recharging_voltage && - !battery->recharging) { - battery->recharging = true; - android_bat_enable_charging(battery, true); - pr_info("battery: start recharging, v=%d\n", - battery->batt_vcell/1000); - } - break; - case POWER_SUPPLY_STATUS_DISCHARGING: - break; - case POWER_SUPPLY_STATUS_CHARGING: - switch (battery->batt_health) { - case POWER_SUPPLY_HEALTH_OVERHEAT: - case POWER_SUPPLY_HEALTH_COLD: - case POWER_SUPPLY_HEALTH_OVERVOLTAGE: - case POWER_SUPPLY_HEALTH_DEAD: - case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: - battery->charging_status = - POWER_SUPPLY_STATUS_NOT_CHARGING; - android_bat_enable_charging(battery, false); - - pr_info("battery: Not charging, health=%d\n", - battery->batt_health); - break; - default: - break; - } - break; - case POWER_SUPPLY_STATUS_NOT_CHARGING: - if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) { - pr_info("battery: battery health recovered\n"); - if (battery->charge_source != CHARGE_SOURCE_NONE) { - android_bat_enable_charging(battery, true); - battery->charging_status - = POWER_SUPPLY_STATUS_CHARGING; - } else { - battery->charging_status - = POWER_SUPPLY_STATUS_DISCHARGING; - } - } - break; - default: - pr_err("%s: Undefined battery status: %d\n", __func__, - battery->charging_status); - break; - } - - android_bat_charging_timer(battery); - get_monotonic_boottime(&cur_time); - pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", - battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp < 0 ? "-" : "", - abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), - battery->batt_health, battery->charging_status, - battery->recharging ? "r" : "", - battery->charging_start_time ? - cur_time.tv_sec - battery->charging_start_time : 0, - charge_source_str(battery->charge_source)); - mutex_unlock(&android_bat_state_lock); - power_supply_changed(&battery->psy_bat); - battery->last_poll = ktime_get_boottime(); - android_bat_monitor_set_alarm(battery, FAST_POLL); - wake_unlock(&battery->monitor_wake_lock); - return; -} - -static enum alarmtimer_restart android_bat_monitor_alarm( - struct alarm *alarm, ktime_t now) -{ - struct android_bat_data *battery = - container_of(alarm, struct android_bat_data, monitor_alarm); - - wake_lock(&battery->monitor_wake_lock); - queue_work(battery->monitor_wqueue, &battery->monitor_work); - return ALARMTIMER_NORESTART; -} - -static int android_power_debug_dump(struct seq_file *s, void *unused) -{ - struct android_bat_data *battery = s->private; - struct timespec cur_time; - - android_bat_update_data(battery); - get_monotonic_boottime(&cur_time); - mutex_lock(&android_bat_state_lock); - seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", - battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp < 0 ? "-" : "", - abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), - battery->batt_health, battery->charging_status, - battery->recharging ? "r" : "", - battery->charging_start_time ? - cur_time.tv_sec - battery->charging_start_time : 0, - charge_source_str(battery->charge_source)); - mutex_unlock(&android_bat_state_lock); - return 0; -} - -static int android_power_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, android_power_debug_dump, inode->i_private); -} - -static const struct file_operations android_power_debug_fops = { - .open = android_power_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int android_bat_probe(struct platform_device *pdev) -{ - struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct android_bat_data *battery; - int ret = 0; - - dev_info(&pdev->dev, "Android Battery Driver\n"); - battery = kzalloc(sizeof(*battery), GFP_KERNEL); - if (!battery) - return -ENOMEM; - - battery->pdata = pdata; - if (!battery->pdata) { - pr_err("%s : No platform data\n", __func__); - ret = -EINVAL; - goto err_pdata; - } - - battery->dev = &pdev->dev; - platform_set_drvdata(pdev, battery); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - - battery->psy_bat.name = "android-battery", - battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY, - battery->psy_bat.properties = android_battery_props, - battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), - battery->psy_bat.get_property = android_bat_get_property, - - battery->batt_vcell = -1; - battery->batt_soc = -1; - - wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, - "android-battery-monitor"); - wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND, - "android-chargerdetect"); - - ret = power_supply_register(&pdev->dev, &battery->psy_bat); - if (ret) { - dev_err(battery->dev, "%s: failed to register psy_bat\n", - __func__); - goto err_psy_bat_reg; - } - - battery->monitor_wqueue = - alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); - if (!battery->monitor_wqueue) { - dev_err(battery->dev, "%s: fail to create workqueue\n", - __func__); - goto err_wq; - } - - INIT_WORK(&battery->monitor_work, android_bat_monitor_work); - INIT_WORK(&battery->charger_work, android_bat_charger_work); - - battery->callbacks.charge_source_changed = - android_bat_charge_source_changed; - battery->callbacks.battery_set_full = - android_bat_set_full_status; - if (battery->pdata && battery->pdata->register_callbacks) - battery->pdata->register_callbacks(&battery->callbacks); - - /* get initial charger status */ - if (battery->pdata->poll_charge_source) - battery->charge_source = battery->pdata->poll_charge_source(); - - wake_lock(&battery->charger_wake_lock); - queue_work(battery->monitor_wqueue, &battery->charger_work); - - wake_lock(&battery->monitor_wake_lock); - battery->last_poll = ktime_get_boottime(); - alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, - android_bat_monitor_alarm); - queue_work(battery->monitor_wqueue, &battery->monitor_work); - - battery->debugfs_entry = - debugfs_create_file("android-power", S_IRUGO, NULL, - battery, &android_power_debug_fops); - if (!battery->debugfs_entry) - pr_err("failed to create android-power debugfs entry\n"); - - return 0; - -err_wq: - power_supply_unregister(&battery->psy_bat); -err_psy_bat_reg: - wake_lock_destroy(&battery->monitor_wake_lock); - wake_lock_destroy(&battery->charger_wake_lock); -err_pdata: - kfree(battery); - - return ret; -} - -static int android_bat_remove(struct platform_device *pdev) -{ - struct android_bat_data *battery = platform_get_drvdata(pdev); - - alarm_cancel(&battery->monitor_alarm); - flush_workqueue(battery->monitor_wqueue); - destroy_workqueue(battery->monitor_wqueue); - power_supply_unregister(&battery->psy_bat); - wake_lock_destroy(&battery->monitor_wake_lock); - wake_lock_destroy(&battery->charger_wake_lock); - debugfs_remove(battery->debugfs_entry); - kfree(battery); - return 0; -} - -static int android_bat_suspend(struct device *dev) -{ - struct android_bat_data *battery = dev_get_drvdata(dev); - - cancel_work_sync(&battery->monitor_work); - android_bat_monitor_set_alarm( - battery, - battery->charge_source == CHARGE_SOURCE_NONE ? - SLOW_POLL : FAST_POLL); - return 0; -} - -static void android_bat_resume(struct device *dev) -{ - struct android_bat_data *battery = dev_get_drvdata(dev); - - android_bat_monitor_set_alarm(battery, FAST_POLL); - return; -} - -static const struct dev_pm_ops android_bat_pm_ops = { - .prepare = android_bat_suspend, - .complete = android_bat_resume, -}; - -static struct platform_driver android_bat_driver = { - .driver = { - .name = "android-battery", - .owner = THIS_MODULE, - .pm = &android_bat_pm_ops, - }, - .probe = android_bat_probe, - .remove = android_bat_remove, -}; - -static int __init android_bat_init(void) -{ - return platform_driver_register(&android_bat_driver); -} - -static void __exit android_bat_exit(void) -{ - platform_driver_unregister(&android_bat_driver); -} - -late_initcall(android_bat_init); -module_exit(android_bat_exit); - -MODULE_DESCRIPTION("Android battery driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h index 585040be5f18..aba25cbb0382 100644 --- a/drivers/staging/android/sw_sync.h +++ b/drivers/staging/android/sw_sync.h @@ -21,6 +21,7 @@ #ifdef __KERNEL__ +#include #include "sync.h" struct sw_sync_timeline { @@ -35,10 +36,27 @@ struct sw_sync_pt { u32 value; }; +#if IS_ENABLED(CONFIG_SW_SYNC) struct sw_sync_timeline *sw_sync_timeline_create(const char *name); void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); +#else +static inline struct sw_sync_timeline *sw_sync_timeline_create(const char *name) +{ + return NULL; +} + +static inline void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) +{ +} + +static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, + u32 value) +{ + return NULL; +} +#endif /* IS_ENABLED(CONFIG_SW_SYNC) */ #endif /* __KERNEL __ */ diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c index 3893a3574769..d38305b40930 100644 --- a/drivers/staging/android/sync.c +++ b/drivers/staging/android/sync.c @@ -79,27 +79,27 @@ static void sync_timeline_free(struct kref *kref) container_of(kref, struct sync_timeline, kref); unsigned long flags; - if (obj->ops->release_obj) - obj->ops->release_obj(obj); - spin_lock_irqsave(&sync_timeline_list_lock, flags); list_del(&obj->sync_timeline_list); spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + if (obj->ops->release_obj) + obj->ops->release_obj(obj); + kfree(obj); } void sync_timeline_destroy(struct sync_timeline *obj) { obj->destroyed = true; + smp_wmb(); /* - * If this is not the last reference, signal any children - * that their parent is going away. + * signal any children that their parent is going away. */ + sync_timeline_signal(obj); - if (!kref_put(&obj->kref, sync_timeline_free)) - sync_timeline_signal(obj); + kref_put(&obj->kref, sync_timeline_free); } EXPORT_SYMBOL(sync_timeline_destroy); diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c index e05fc2591147..e373b625806e 100644 --- a/drivers/switch/switch_class.c +++ b/drivers/switch/switch_class.c @@ -54,8 +54,8 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", sdev->name); } -static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); -static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); +static DEVICE_ATTR(name, S_IRUGO, name_show, NULL); void switch_set_state(struct switch_dev *sdev, int state) { diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index d0ec54b810c1..04cbeb134814 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -1490,7 +1490,7 @@ static int __init init(void) err = usb_composite_probe(&android_usb_driver); if (err) { pr_err("%s: failed to probe driver %d", __func__, err); - goto err_create; + goto err_probe; } /* HACK: exchange composite's setup with ours */ @@ -1499,6 +1499,8 @@ static int __init init(void) return 0; +err_probe: + device_destroy(android_class, dev->dev->devt); err_create: kfree(dev); err_dev: diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 9ab94697c196..12fb818ab147 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -410,15 +410,6 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, intr_desc); if (!ep) { DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 7d23792d772c..50f7afe56553 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2497,6 +2497,7 @@ source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" +source "drivers/video/adf/Kconfig" if VT source "drivers/video/console/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 33869eea4981..3adbd32eb091 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -12,6 +12,7 @@ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ modedb.o fbcvt.o fb-objs := $(fb-y) +obj-$(CONFIG_ADF) += adf/ obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig new file mode 100644 index 000000000000..33858b73d8bb --- /dev/null +++ b/drivers/video/adf/Kconfig @@ -0,0 +1,14 @@ +menuconfig ADF + depends on SYNC + depends on DMA_SHARED_BUFFER + tristate "Atomic Display Framework" + +menuconfig ADF_FBDEV + depends on ADF + depends on FB + tristate "Helper for implementing the fbdev API in ADF drivers" + +menuconfig ADF_MEMBLOCK + depends on ADF + depends on HAVE_MEMBLOCK + tristate "Helper for using memblocks as buffers in ADF drivers" diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile new file mode 100644 index 000000000000..78d0915122f4 --- /dev/null +++ b/drivers/video/adf/Makefile @@ -0,0 +1,15 @@ +ccflags-y := -Idrivers/staging/android + +CFLAGS_adf.o := -I$(src) + +obj-$(CONFIG_ADF) += adf.o \ + adf_client.o \ + adf_fops.o \ + adf_format.o \ + adf_sysfs.o + +obj-$(CONFIG_COMPAT) += adf_fops32.o + +obj-$(CONFIG_ADF_FBDEV) += adf_fbdev.o + +obj-$(CONFIG_ADF_MEMBLOCK) += adf_memblock.o diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c new file mode 100644 index 000000000000..fd5bcde87850 --- /dev/null +++ b/drivers/video/adf/adf.c @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2013 Google, Inc. + * adf_modeinfo_{set_name,set_vrefresh} modified from + * drivers/gpu/drm/drm_modes.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include